mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-13 01:59:29 +00:00
Refactor FLEX RX UI to use console for messages (#2890)
This commit is contained in:
-84
@@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2025 timelf123
|
|
||||||
* with barely any help from RocketGod but I exist.
|
|
||||||
*
|
|
||||||
* 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_flex_rx.hpp"
|
|
||||||
#include "ui_navigation.hpp"
|
|
||||||
#include "external_app.hpp"
|
|
||||||
|
|
||||||
namespace ui::external_app::flex_rx {
|
|
||||||
void initialize_app(ui::NavigationView& nav) {
|
|
||||||
nav.push<FlexAppView>();
|
|
||||||
}
|
|
||||||
} // namespace ui::external_app::flex_rx
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
__attribute__((section(".external_app.app_flex_rx.application_information"), used)) application_information_t _application_information_flex_rx = {
|
|
||||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
|
||||||
/*.externalAppEntry = */ ui::external_app::flex_rx::initialize_app,
|
|
||||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
|
||||||
/*.app_version = */ VERSION_MD5,
|
|
||||||
|
|
||||||
/*.app_name = */ "FLEX RX",
|
|
||||||
/*.bitmap_data = */ {
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0xFE,
|
|
||||||
0x7F,
|
|
||||||
0x02,
|
|
||||||
0x40,
|
|
||||||
0xFA,
|
|
||||||
0x5F,
|
|
||||||
0x02,
|
|
||||||
0x40,
|
|
||||||
0xF2,
|
|
||||||
0x4F,
|
|
||||||
0x02,
|
|
||||||
0x40,
|
|
||||||
0xE2,
|
|
||||||
0x47,
|
|
||||||
0x02,
|
|
||||||
0x40,
|
|
||||||
0xC2,
|
|
||||||
0x43,
|
|
||||||
0x02,
|
|
||||||
0x40,
|
|
||||||
0x82,
|
|
||||||
0x41,
|
|
||||||
0x02,
|
|
||||||
0x40,
|
|
||||||
0xFE,
|
|
||||||
0x7F,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
0x00,
|
|
||||||
},
|
|
||||||
/*.icon_color = */ ui::Color::cyan().v,
|
|
||||||
/*.menu_location = */ app_location_t::RX,
|
|
||||||
/*.desired_menu_position = */ -1,
|
|
||||||
|
|
||||||
/*.m4_app_tag = portapack::spi_flash::image_tag_flex */ {'P', 'F', 'L', 'X'},
|
|
||||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
|
||||||
};
|
|
||||||
}
|
|
||||||
+46
-84
@@ -18,9 +18,8 @@ FlexAppView::FlexAppView(NavigationView& nav)
|
|||||||
&field_rf_amp,
|
&field_rf_amp,
|
||||||
&field_lna,
|
&field_lna,
|
||||||
&field_vga,
|
&field_vga,
|
||||||
&button_color,
|
|
||||||
&rssi,
|
&rssi,
|
||||||
&menu_view});
|
&console});
|
||||||
|
|
||||||
// Restore saved frequency
|
// Restore saved frequency
|
||||||
field_frequency.set_value(frequency_value);
|
field_frequency.set_value(frequency_value);
|
||||||
@@ -31,11 +30,6 @@ FlexAppView::FlexAppView(NavigationView& nav)
|
|||||||
update_freq(f);
|
update_freq(f);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Color button cycles through available colors
|
|
||||||
button_color.on_select = [this](Button&) {
|
|
||||||
cycle_color();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Configure receiver
|
// Configure receiver
|
||||||
receiver_model.set_sampling_rate(3072000);
|
receiver_model.set_sampling_rate(3072000);
|
||||||
receiver_model.set_baseband_bandwidth(1750000);
|
receiver_model.set_baseband_bandwidth(1750000);
|
||||||
@@ -45,7 +39,7 @@ FlexAppView::FlexAppView(NavigationView& nav)
|
|||||||
// Initialize FLEX baseband
|
// Initialize FLEX baseband
|
||||||
baseband::set_flex_config();
|
baseband::set_flex_config();
|
||||||
|
|
||||||
log_message("FLEX RX Ready");
|
console.writeln("Ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
FlexAppView::~FlexAppView() {
|
FlexAppView::~FlexAppView() {
|
||||||
@@ -57,79 +51,53 @@ void FlexAppView::focus() {
|
|||||||
field_frequency.focus();
|
field_frequency.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cycle to next text color and refresh display
|
// Redraw all messages to console
|
||||||
void FlexAppView::cycle_color() {
|
void FlexAppView::redraw_console() {
|
||||||
current_color_index = (current_color_index + 1) % text_colors.size();
|
console.clear(true);
|
||||||
rebuild_menu();
|
bool first = true;
|
||||||
}
|
for (const auto& msg : messages) {
|
||||||
|
if (!first) {
|
||||||
// Rebuild entire menu with current color
|
console.writeln(""); // Blank line between messages
|
||||||
void FlexAppView::rebuild_menu() {
|
}
|
||||||
menu_view.clear();
|
first = false;
|
||||||
Color current_color = text_colors[current_color_index];
|
console.writeln(msg);
|
||||||
for (const auto& msg : log_messages) {
|
|
||||||
menu_view.add_item({msg,
|
|
||||||
current_color,
|
|
||||||
nullptr,
|
|
||||||
[](KeyEvent) {}});
|
|
||||||
}
|
|
||||||
if (menu_view.item_count() > 0) {
|
|
||||||
menu_view.set_highlighted(menu_view.item_count() - 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add message to log with automatic line wrapping
|
// Add message to log with automatic line wrapping
|
||||||
void FlexAppView::log_message(const std::string& message) {
|
void FlexAppView::log_message(const std::string& message) {
|
||||||
// Calculate characters per line based on screen width (8 pixels per char)
|
|
||||||
const size_t chars_per_line = screen_width / 8;
|
const size_t chars_per_line = screen_width / 8;
|
||||||
Color current_color = text_colors[current_color_index];
|
// Console height accounts for status bar and controls row
|
||||||
|
const size_t console_lines = (screen_height - 2 * 16) / 16;
|
||||||
|
|
||||||
std::string remaining = message;
|
messages.push_back(message);
|
||||||
bool first_line = true;
|
|
||||||
bool needs_rebuild = false;
|
|
||||||
size_t lines_added = 0;
|
|
||||||
|
|
||||||
// Split message into screen-width chunks
|
// Calculate total lines used (messages + blank lines between them)
|
||||||
while (!remaining.empty()) {
|
size_t total_lines = 0;
|
||||||
std::string line;
|
for (size_t i = 0; i < messages.size(); i++) {
|
||||||
if (remaining.length() <= chars_per_line) {
|
if (i > 0) total_lines++; // Count blank line separator
|
||||||
line = remaining;
|
size_t msg_lines = (messages[i].length() + chars_per_line - 1) / chars_per_line;
|
||||||
remaining.clear();
|
if (msg_lines == 0) msg_lines = 1;
|
||||||
} else {
|
total_lines += msg_lines;
|
||||||
line = remaining.substr(0, chars_per_line);
|
|
||||||
remaining = remaining.substr(chars_per_line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indent continuation lines
|
|
||||||
if (!first_line) {
|
|
||||||
line = " " + line;
|
|
||||||
}
|
|
||||||
first_line = false;
|
|
||||||
|
|
||||||
// Remove oldest line if at limit
|
|
||||||
if (log_messages.size() >= MAX_LOG_LINES) {
|
|
||||||
log_messages.erase(log_messages.begin());
|
|
||||||
needs_rebuild = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_messages.push_back(line);
|
|
||||||
lines_added++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Either rebuild all or just add new lines
|
// If console would overflow, remove oldest messages and redraw
|
||||||
if (needs_rebuild) {
|
if (total_lines > console_lines) {
|
||||||
rebuild_menu();
|
while (total_lines > console_lines && !messages.empty()) {
|
||||||
|
const auto& oldest = messages.front();
|
||||||
|
size_t oldest_lines = (oldest.length() + chars_per_line - 1) / chars_per_line;
|
||||||
|
if (oldest_lines == 0) oldest_lines = 1;
|
||||||
|
total_lines -= oldest_lines;
|
||||||
|
if (messages.size() > 1) total_lines--; // Remove separator line too
|
||||||
|
messages.erase(messages.begin());
|
||||||
|
}
|
||||||
|
redraw_console();
|
||||||
} else {
|
} else {
|
||||||
size_t start_idx = log_messages.size() - lines_added;
|
// Just append new message
|
||||||
for (size_t i = start_idx; i < log_messages.size(); i++) {
|
if (messages.size() > 1) {
|
||||||
menu_view.add_item({log_messages[i],
|
console.writeln(""); // Blank line before new message
|
||||||
current_color,
|
|
||||||
nullptr,
|
|
||||||
[](KeyEvent) {}});
|
|
||||||
}
|
|
||||||
if (menu_view.item_count() > 0) {
|
|
||||||
menu_view.set_highlighted(menu_view.item_count() - 1);
|
|
||||||
}
|
}
|
||||||
|
console.writeln(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,27 +109,21 @@ void FlexAppView::update_freq(rf::Frequency f) {
|
|||||||
|
|
||||||
// Handle decoded FLEX packet from baseband
|
// Handle decoded FLEX packet from baseband
|
||||||
void FlexAppView::on_packet(const FlexPacketMessage* message) {
|
void FlexAppView::on_packet(const FlexPacketMessage* message) {
|
||||||
std::string text = "FLEX ";
|
log_message(message->packet.message);
|
||||||
text += to_string_dec_uint(message->packet.bitrate);
|
|
||||||
text += " ";
|
|
||||||
text += to_string_dec_uint(message->packet.capcode);
|
|
||||||
text += ": ";
|
|
||||||
text += message->packet.message;
|
|
||||||
|
|
||||||
log_message(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle stats message (currently unused)
|
// Handle stats message (currently unused)
|
||||||
void FlexAppView::on_stats(const FlexStatsMessage* /* message */) {
|
void FlexAppView::on_stats(const FlexStatsMessage*) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle debug message from baseband
|
// Debug handler - uncomment to see baseband debug messages
|
||||||
void FlexAppView::on_debug(const FlexDebugMessage* message) {
|
void FlexAppView::on_debug(const FlexDebugMessage* message) {
|
||||||
std::string text = "DBG: ";
|
(void)message; // Suppress unused parameter warning
|
||||||
text += message->text;
|
// std::string text = "DBG: ";
|
||||||
text += " " + to_string_hex(message->val1, 8);
|
// text += message->text;
|
||||||
text += " " + to_string_hex(message->val2, 8);
|
// text += " " + to_string_hex(message->val1, 8);
|
||||||
log_message(text);
|
// text += " " + to_string_hex(message->val2, 8);
|
||||||
|
// log_message(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ui::external_app::flex_rx
|
} // namespace ui::external_app::flex_rx
|
||||||
+11
-30
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <array>
|
|
||||||
|
|
||||||
namespace ui::external_app::flex_rx {
|
namespace ui::external_app::flex_rx {
|
||||||
|
|
||||||
@@ -28,28 +27,17 @@ class FlexAppView : public View {
|
|||||||
|
|
||||||
// Saved settings
|
// Saved settings
|
||||||
rf::Frequency frequency_value{931740000}; // Default FLEX frequency
|
rf::Frequency frequency_value{931740000}; // Default FLEX frequency
|
||||||
uint32_t current_color_index{0}; // Current text color selection
|
|
||||||
|
|
||||||
// Available text colors for message display
|
|
||||||
static constexpr std::array<Color, 7> text_colors = {{Color::green(),
|
|
||||||
Color::white(),
|
|
||||||
Color::cyan(),
|
|
||||||
Color::magenta(),
|
|
||||||
Color::yellow(),
|
|
||||||
Color::blue(),
|
|
||||||
Color::red()}};
|
|
||||||
|
|
||||||
RxRadioState radio_state_{};
|
RxRadioState radio_state_{};
|
||||||
|
|
||||||
// Message log settings
|
// Message storage for console redraw
|
||||||
static constexpr size_t MAX_LOG_LINES = 32; // Limit to prevent memory issues
|
static constexpr size_t MAX_MESSAGES = 20;
|
||||||
std::vector<std::string> log_messages{}; // Stored log lines
|
std::vector<std::string> messages{};
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
void log_message(const std::string& message); // Add message with word wrap
|
void log_message(const std::string& message);
|
||||||
void rebuild_menu(); // Rebuild menu after color change or overflow
|
void redraw_console();
|
||||||
void update_freq(rf::Frequency f); // Update tuned frequency
|
void update_freq(rf::Frequency f);
|
||||||
void cycle_color(); // Cycle through text colors
|
|
||||||
|
|
||||||
// UI Elements - Row 0, dynamically positioned
|
// UI Elements - Row 0, dynamically positioned
|
||||||
RxFrequencyField field_frequency{
|
RxFrequencyField field_frequency{
|
||||||
@@ -63,25 +51,18 @@ class FlexAppView : public View {
|
|||||||
VGAGainField field_vga{
|
VGAGainField field_vga{
|
||||||
{UI_POS_X(18), UI_POS_Y(0)}};
|
{UI_POS_X(18), UI_POS_Y(0)}};
|
||||||
|
|
||||||
// Color cycle button
|
|
||||||
Button button_color{
|
|
||||||
{UI_POS_X(21), UI_POS_Y(0), UI_POS_WIDTH(5), UI_POS_HEIGHT(1)},
|
|
||||||
"COLOR"};
|
|
||||||
|
|
||||||
RSSI rssi{
|
RSSI rssi{
|
||||||
{UI_POS_X(26), 0, UI_POS_WIDTH(4), 4}};
|
{UI_POS_X(21), 0, UI_POS_WIDTH(9), 4}};
|
||||||
|
|
||||||
// Message display area - scrollable menu view
|
// Message display area (below controls, account for status bar)
|
||||||
MenuView menu_view{
|
Console console{
|
||||||
{0, 1 * 16, screen_width, screen_height - 1 * 16},
|
{0, 1 * 16, screen_width, screen_height - 2 * 16}};
|
||||||
true};
|
|
||||||
|
|
||||||
// Persistent settings manager
|
// Persistent settings manager
|
||||||
app_settings::SettingsManager settings_{
|
app_settings::SettingsManager settings_{
|
||||||
"rx_flex",
|
"rx_flex",
|
||||||
app_settings::Mode::RX,
|
app_settings::Mode::RX,
|
||||||
{{"frequency", &frequency_value},
|
{{"frequency", &frequency_value}}};
|
||||||
{"color_index", ¤t_color_index}}};
|
|
||||||
|
|
||||||
// Message handlers
|
// Message handlers
|
||||||
void on_packet(const FlexPacketMessage* message);
|
void on_packet(const FlexPacketMessage* message);
|
||||||
|
|||||||
Reference in New Issue
Block a user