Compare commits

...

14 Commits

Author SHA1 Message Date
zxkmm 6c37d883a0 merge new changes 2024-04-30 23:05:53 +08:00
Mark Thompson e43f7a7980
Change flashing message to match LED behavior (#2130) 2024-04-29 22:29:46 +02:00
Totoo f6a2d21624
support for battery if any + TPMS app to ext (#2129)
* batt initial
* batt widgets
* settings modify
* batt info screen
* ability to hide icon
* battView
* redo tmps
* hide curr + charge if no data
* fix flashing
2024-04-29 17:38:27 +02:00
sommermorgentraum f572b00391
add real gcc ver chk (#2118) 2024-04-29 17:36:30 +02:00
Totoo d74fd527e2
TPMS to ext app (#2128)
* initial

* fix
2024-04-29 13:30:21 +02:00
zxkmm 5d7cfda97b merge new changes 2024-04-28 15:33:50 +08:00
sommermorgentraum 819f35ac93
seperate asyncmsg (#2124) 2024-04-27 21:24:41 +02:00
Lpd738 60da661999
Update JAMMER_GSM_GPS.TXT (#2125)
Add L5 GPS range
2024-04-27 11:50:08 +08:00
jLynx 43077c79cf
Updated submodule (#2121) 2024-04-26 19:49:21 +12:00
Totoo 06651dc97c
Bmp File Viewer + extras (#2119)
* BMP initial

* Add vscode debug configuration as a template (#2109)

* usb serial debug interface & usb serial async msg (#2111)

* add serial_debug

* not use OSS

* add path print

* add string print and vec

* clean up

* clean up

* format

* add an async blocking bool

* add an async blocking bool - comment

* protect the unexpected tx

* naming

* remove demo code

* fix bottom-up format, and add auto extend, ..

* bmp write

* Minor additions

* Minor

* overwrite on create

* Tmp

* Basic view - WIP

* debug

* add literal str print in asyncmsg (#2113)

* add literal str print in asyncmsg

* remove debug things

* accept suggestion per gull

* fix documentary

* Fix bug (#2114)

* Disable Back button during Touch Calibration (#2115)

* ADS1100 (#2116)

* WIP

* WIP

* WIP

* Corrected name

* WIP

* WIP

* WIP

* WIP

* Added new calc

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* Added debug serial lines

* WIP

* Fixed issue

* Fixed calculation issue

* Added voltage to performance DFU menu

* Added padding function and added voltage to perf menu

* Clean up

* Refactor

* Fixed linting

* Hides voltage if PP does not conatin IC

* WIP showing battery %

* made the percentage a int

* Added % to header

* Removed test UI

* Removed comment

* Added fix for precision too large

* Added fix for precision too large

* Linting

* widget

* auto zoom

* remove debug

* move in screen

* fix math

* remove test code

* fix

* fix compiler warning

* BMP File viewer

* Full screen

* bg instead of noice

* add comment

* Handle some not supported formats.

---------

Co-authored-by: E.T <tamas@eisenberger.hu>
Co-authored-by: sommermorgentraum <24917424+zxkmm@users.noreply.github.com>
Co-authored-by: Mark Thompson <129641948+NotherNgineer@users.noreply.github.com>
Co-authored-by: jLynx <admin@jlynx.net>
2024-04-24 05:18:12 +02:00
jLynx 67975d76a0
ADS1100 (#2116)
* WIP

* WIP

* WIP

* Corrected name

* WIP

* WIP

* WIP

* WIP

* Added new calc

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* Added debug serial lines

* WIP

* Fixed issue

* Fixed calculation issue

* Added voltage to performance DFU menu

* Added padding function and added voltage to perf menu

* Clean up

* Refactor

* Fixed linting

* Hides voltage if PP does not conatin IC

* WIP showing battery %

* made the percentage a int

* Added % to header

* Removed test UI

* Removed comment

* Added fix for precision too large

* Added fix for precision too large

* Linting
2024-04-21 20:44:47 +12:00
Mark Thompson 282e4da1cb
Disable Back button during Touch Calibration (#2115) 2024-04-21 08:34:55 +02:00
Totoo e7359563c6
Fix bug (#2114) 2024-04-20 14:26:54 +02:00
sommermorgentraum 03ca87b13e
add literal str print in asyncmsg (#2113)
* add literal str print in asyncmsg

* remove debug things

* accept suggestion per gull

* fix documentary
2024-04-20 18:31:57 +08:00
52 changed files with 2265 additions and 244 deletions

1
.gitignore vendored
View File

@ -57,6 +57,7 @@
.dep/
/build*
CMakeFiles/
cmake-build-debug/
# Debugging
.gdbinit*

View File

@ -176,7 +176,10 @@ set(CPPSRC
${COMMON}/ui_language.cpp
${COMMON}/utility.cpp
${COMMON}/wm8731.cpp
${COMMON}/ads1110.cpp
${COMMON}/battery.cpp
${COMMON}/performance_counter.cpp
${COMMON}/bmpfile.cpp
app_settings.cpp
audio.cpp
baseband_api.cpp
@ -209,6 +212,7 @@ set(CPPSRC
usb_serial_thread.cpp
usb_serial.cpp
usb_serial_host_to_device.cpp
usb_serial_asyncmsg.cpp
qrcodegen.cpp
radio.cpp
receiver_model.cpp
@ -225,7 +229,6 @@ set(CPPSRC
tone_key.cpp
transmitter_model.cpp
tuning.cpp
usb_serial_asyncmsg.hpp
hw/debounce.cpp
hw/encoder.cpp
hw/max2837.cpp
@ -260,6 +263,7 @@ set(CPPSRC
ui/ui_textentry.cpp
ui/ui_tone_key.cpp
ui/ui_transmitter.cpp
ui/ui_bmpview.cpp
apps/acars_app.cpp
apps/ais_app.cpp
apps/analog_audio_app.cpp
@ -274,14 +278,15 @@ set(CPPSRC
apps/pocsag_app.cpp
# apps/replay_app.cpp
apps/soundboard_app.cpp
apps/tpms_app.cpp
apps/tpms_app.cpp
# apps/tpms_app.cpp
apps/ui_about_simple.cpp
apps/ui_adsb_rx.cpp
apps/ui_adsb_tx.cpp
apps/ui_aprs_rx.cpp
apps/ui_aprs_tx.cpp
apps/ui_battinfo.cpp
apps/ui_bht_tx.cpp
apps/ui_bmp_file_viewer.cpp
apps/ui_btle_rx.cpp
# apps/ui_coasterp.cpp
apps/ui_debug.cpp

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_battinfo.hpp"
#include "event_m0.hpp"
#include "portapack.hpp"
#include <cstring>
using namespace portapack;
namespace ui {
void BattinfoView::focus() {
button_exit.focus();
}
// called each 1/60th of second, so 6 = 100ms
void BattinfoView::on_timer() {
if (++timer_counter == timer_period) {
timer_counter = 0;
update_result();
}
}
void BattinfoView::update_result() {
if (!battery::BatteryManagement::isDetected()) {
// todo show no batt management
text_percent.set("UNKNOWN");
text_voltage.set("UNKNOWN");
text_current.set("-");
text_charge.set("-");
return;
}
bool uichg = false;
battery::BatteryManagement::getBatteryInfo(percent, voltage, current, isCharging);
// update text fields
if (percent <= 100)
text_percent.set(to_string_dec_uint(percent) + " %");
else
text_percent.set("UNKNOWN");
if (voltage > 1) {
text_voltage.set(to_string_decimal(voltage / 1000.0, 3) + " V");
} else {
text_voltage.set("UNKNOWN");
}
if (current != 0) {
if (labels_opt.hidden()) uichg = true;
labels_opt.hidden(false);
text_current.hidden(false);
text_charge.hidden(false);
text_current.set(to_string_dec_int(current) + " mA");
text_charge.set(isCharging ? "charge" : "discharge");
labels_opt.hidden(false);
} else {
if (!labels_opt.hidden()) uichg = true;
labels_opt.hidden(true);
text_current.hidden(true);
text_charge.hidden(true);
}
if (uichg) set_dirty();
// to update status bar too, send message in behalf of batt manager
BatteryStateMessage msg{percent, isCharging, voltage};
EventDispatcher::send_message(msg);
}
BattinfoView::BattinfoView(NavigationView& nav)
: nav_{nav} {
add_children({&labels,
&labels_opt,
&text_percent,
&text_voltage,
&text_current,
&text_charge,
&button_exit});
button_exit.on_select = [this, &nav](Button&) {
nav.pop();
};
update_result();
}
} // namespace ui

View File

@ -0,0 +1,83 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_BATTINFO_H__
#define __UI_BATTINFO_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
namespace ui {
class BattinfoView : public View {
public:
BattinfoView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Battery"; };
private:
void update_result();
void on_timer();
NavigationView& nav_;
uint16_t timer_period = 60;
uint16_t timer_counter = 0;
uint8_t percent = 0;
uint16_t voltage = 0;
int32_t current = 0;
bool isCharging = false;
Labels labels{
{{2 * 8, 1 * 16}, "Percent:", Color::light_grey()},
{{2 * 8, 2 * 16}, "Voltage:", Color::light_grey()}};
Labels labels_opt{
{{2 * 8, 3 * 16}, "Current:", Color::light_grey()},
{{2 * 8, 4 * 16}, "Charge:", Color::light_grey()}};
Text text_percent{
{13 * 8, 1 * 16, 10 * 16, 16},
"-"};
Text text_voltage{
{13 * 8, 2 * 16, 10 * 16, 16},
"-"};
Text text_current{
{13 * 8, 3 * 16, 10 * 16, 16},
"-"};
Text text_charge{
{13 * 8, 4 * 16, 10 * 16, 16},
"-"};
Button button_exit{
{72, 17 * 16, 96, 32},
"Back"};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->on_timer();
}};
};
} /* namespace ui */
#endif /*__UI_BATTINFO__*/

View File

@ -0,0 +1,63 @@
/*
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_bmp_file_viewer.hpp"
extern ui::SystemView* system_view_ptr;
using namespace portapack;
namespace fs = std::filesystem;
namespace ui {
BMPFileViewer::BMPFileViewer(
NavigationView& nav,
const std::filesystem::path& path)
: nav_{nav},
path_{path} {
add_children(
{&bmp});
bmp.set_enter_pass(true); // pass the enter key to me, so i can exit. this will disable zoom + pos reset
set_focusable(true);
system_view_ptr->set_app_fullscreen(true);
}
BMPFileViewer::~BMPFileViewer() {
system_view_ptr->set_app_fullscreen(false);
}
void BMPFileViewer::focus() {
bmp.focus();
}
bool BMPFileViewer::on_key(KeyEvent k) {
if (k == KeyEvent::Select) {
nav_.pop();
return true;
}
return false;
}
void BMPFileViewer::paint(Painter&) {
bmp.load_bmp(path_);
}
} // namespace ui

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_BMP_FILE_VIEWER_H__
#define __UI_BMP_FILE_VIEWER_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_painter.hpp"
#include "ui_styles.hpp"
#include "ui_widget.hpp"
#include "file.hpp"
#include "ui_bmpview.hpp"
namespace ui {
class BMPFileViewer : public View {
public:
BMPFileViewer(NavigationView& nav, const std::filesystem::path& path);
~BMPFileViewer();
bool on_key(KeyEvent key) override;
void paint(Painter& painter) override;
void focus() override;
private:
NavigationView& nav_;
std::filesystem::path path_{};
BMPViewer bmp{{0, 0, 240, 320}};
};
} // namespace ui
#endif // __UI_BMP_FILE_VIEWER_H__

View File

@ -41,6 +41,11 @@ DfuMenu::DfuMenu(NavigationView& nav)
&text_info_line_8,
&text_info_line_9,
&text_info_line_10});
if (battery::BatteryManagement::isDetected()) {
add_child(&voltage_label);
add_child(&text_info_line_11);
}
}
void DfuMenu::paint(Painter& painter) {
@ -48,6 +53,8 @@ void DfuMenu::paint(Painter& painter) {
size_t m0_fragmented_free_space = 0;
const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space);
auto lines = (battery::BatteryManagement::isDetected() ? 11 : 10) + 2;
text_info_line_1.set(to_string_dec_uint(chCoreStatus(), 6));
text_info_line_2.set(to_string_dec_uint(m0_fragmented_free_space, 6));
text_info_line_3.set(to_string_dec_uint(m0_fragments, 6));
@ -58,9 +65,11 @@ void DfuMenu::paint(Painter& painter) {
text_info_line_8.set(to_string_dec_uint(shared_memory.m4_performance_counter, 6));
text_info_line_9.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6));
text_info_line_10.set(to_string_dec_uint(chTimeNow() / 1000, 6));
if (battery::BatteryManagement::isDetected()) {
text_info_line_11.set(to_string_decimal_padding((float)battery::BatteryManagement::getVoltage() / 1000.0, 3, 6));
}
constexpr auto margin = 5;
constexpr auto lines = 10 + 2;
painter.fill_rectangle(
{{6 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},

View File

@ -58,7 +58,9 @@ class DfuMenu : public View {
{{6 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "M4 stack:", Color::dark_cyan()},
{{6 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "M4 cpu %:", Color::dark_cyan()},
{{6 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "M4 miss:", Color::dark_cyan()},
{{6 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "uptime:", Color::dark_cyan()}};
{{6 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "Uptime:", Color::dark_cyan()}};
Labels voltage_label{{{6 * CHARACTER_WIDTH, 15 * LINE_HEIGHT}, "Voltage:", Color::dark_cyan()}};
Text text_info_line_1{{15 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_2{{15 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
@ -70,6 +72,7 @@ class DfuMenu : public View {
Text text_info_line_8{{15 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_9{{15 * CHARACTER_WIDTH, 13 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_10{{15 * CHARACTER_WIDTH, 14 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
Text text_info_line_11{{15 * CHARACTER_WIDTH, 15 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
};
class DfuMenu2 : public View {

View File

@ -30,6 +30,7 @@
#include "ui_playlist.hpp"
#include "ui_remote.hpp"
#include "ui_ss_viewer.hpp"
#include "ui_bmp_file_viewer.hpp"
#include "ui_text_editor.hpp"
#include "ui_iq_trim.hpp"
#include "string_format.hpp"
@ -786,7 +787,12 @@ bool FileManagerView::handle_file_open() {
nav_.push<ScreenshotViewer>(path);
return true;
} else if (path_iequal(bmp_ext, ext)) {
nav_.push<SplashViewer>(path);
if (path_iequal(current_path, u"/" + splash_dir)) {
nav_.push<SplashViewer>(path); // splash, so load that viewer
} else {
nav_.push<BMPFileViewer>(path); // any other bmp
}
reload_current(false);
return true;
} else if (path_iequal(rem_ext, ext)) {

View File

@ -163,8 +163,8 @@ bool FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
ui::Color::black());
painter.draw_string({12, 24}, this->nav_.style(), "This will take 15 seconds.");
painter.draw_string({12, 64}, this->nav_.style(), "Please wait while LEDs RX");
painter.draw_string({12, 84}, this->nav_.style(), "and TX are flashing.");
painter.draw_string({12, 64}, this->nav_.style(), "Please wait while LED RX");
painter.draw_string({12, 84}, this->nav_.style(), "is on and TX is flashing.");
painter.draw_string({12, 124}, this->nav_.style(), "Device will then restart.");
std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (path.length() + 1) * 2);

View File

@ -329,6 +329,10 @@ SetUIView::SetUIView(NavigationView& nav) {
if (audio::speaker_disable_supported()) {
add_child(&toggle_speaker);
}
if (battery::BatteryManagement::isDetected()) {
add_child(&toggle_battery_icon);
add_child(&toggle_battery_text);
}
checkbox_disable_touchscreen.set_value(pmem::disable_touchscreen());
checkbox_showsplash.set_value(pmem::config_splash());
@ -355,6 +359,8 @@ SetUIView::SetUIView(NavigationView& nav) {
toggle_speaker.set_value(!pmem::ui_hide_speaker());
toggle_mute.set_value(!pmem::ui_hide_mute());
toggle_fake_brightness.set_value(!pmem::ui_hide_fake_brightness());
toggle_battery_icon.set_value(!pmem::ui_hide_battery_icon());
toggle_battery_text.set_value(!pmem::ui_hide_numeric_battery());
toggle_sd_card.set_value(!pmem::ui_hide_sd_card());
button_save.on_select = [&nav, this](Button&) {
@ -382,6 +388,8 @@ SetUIView::SetUIView(NavigationView& nav) {
pmem::set_ui_hide_speaker(!toggle_speaker.value());
pmem::set_ui_hide_mute(!toggle_mute.value());
pmem::set_ui_hide_fake_brightness(!toggle_fake_brightness.value());
pmem::set_ui_hide_battery_icon(!toggle_battery_icon.value());
pmem::set_ui_hide_numeric_battery(!toggle_battery_text.value());
pmem::set_ui_hide_sd_card(!toggle_sd_card.value());
send_system_refresh();
@ -807,6 +815,7 @@ SetMenuColorView::SetMenuColorView(NavigationView& nav) {
&field_green_level,
&field_blue_level,
&button_save,
&button_reset,
&button_cancel});
button_sample.set_focusable(false);
@ -824,6 +833,13 @@ SetMenuColorView::SetMenuColorView(NavigationView& nav) {
field_green_level.on_change = color_changed_fn;
field_blue_level.on_change = color_changed_fn;
button_reset.on_select = [&nav, this](Button&) {
field_red_level.set_value(127);
field_green_level.set_value(127);
field_blue_level.set_value(127);
set_dirty();
};
button_save.on_select = [&nav, this](Button&) {
Color c = Color(field_red_level.value(), field_green_level.value(), field_blue_level.value());
pmem::set_menu_color(c);

View File

@ -323,37 +323,45 @@ class SetUIView : public View {
};
ImageToggle toggle_camera{
{6 * 8, 14 * 16 + 2, 16, 16},
{2 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_camera};
ImageToggle toggle_sleep{
{8 * 8, 14 * 16 + 2, 16, 16},
{4 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_sleep};
ImageToggle toggle_stealth{
{10 * 8, 14 * 16 + 2, 16, 16},
{6 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_stealth};
ImageToggle toggle_converter{
{12 * 8, 14 * 16 + 2, 16, 16},
{8 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_upconvert};
ImageToggle toggle_bias_tee{
{14 * 8, 14 * 16 + 2, 16, 16},
{10 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_biast_off};
ImageToggle toggle_clock{
{16 * 8, 14 * 16 + 2, 8, 16},
{12 * 8, 14 * 16 + 2, 8, 16},
&bitmap_icon_clk_ext};
ImageToggle toggle_mute{
{17 * 8, 14 * 16 + 2, 16, 16},
{13 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_speaker_and_headphones_mute};
ImageToggle toggle_speaker{
{19 * 8, 14 * 16 + 2, 16, 16},
{15 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_speaker_mute};
ImageToggle toggle_battery_icon{
{17 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_batt_icon};
ImageToggle toggle_battery_text{
{19 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_batt_text};
ImageToggle toggle_fake_brightness{
{21 * 8, 14 * 16 + 2, 16, 16},
&bitmap_icon_brightness};
@ -789,6 +797,11 @@ class SetMenuColorView : public View {
' ',
};
Button button_reset{
{2 * 8, 13 * 16, 12 * 8, 32},
"Reset",
};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};

View File

@ -26,6 +26,8 @@
#include "portapack_persistent_memory.hpp"
using namespace portapack;
extern ui::SystemView* system_view_ptr;
namespace ui {
TouchCalibrationView::TouchCalibrationView(
@ -40,6 +42,7 @@ TouchCalibrationView::TouchCalibrationView(
&image_verify_1,
&image_verify_2,
&label_calibrate,
&label_calibrate_2,
&label_verify,
&label_success,
&label_failure,
@ -51,6 +54,12 @@ TouchCalibrationView::TouchCalibrationView(
button_ok.on_select = [this](Button&) { this->on_ok(); };
set_phase(Phase::Calibrate0);
system_view_ptr->get_status_view()->set_back_hidden(true);
}
TouchCalibrationView::~TouchCalibrationView() {
system_view_ptr->get_status_view()->set_back_hidden(false);
}
void TouchCalibrationView::focus() {
@ -70,6 +79,7 @@ void TouchCalibrationView::update_target() {
image_verify_2.hidden(phase != Phase::Verify2);
label_calibrate.hidden(!phase_calibrate);
label_calibrate_2.hidden(!phase_calibrate && !phase_verify);
label_verify.hidden(!phase_verify);
label_success.hidden(phase != Phase::Success);
label_failure.hidden(phase != Phase::Failure);

View File

@ -31,6 +31,7 @@ namespace ui {
class TouchCalibrationView : public View {
public:
TouchCalibrationView(NavigationView& nav);
~TouchCalibrationView();
void focus() override;
@ -110,9 +111,13 @@ class TouchCalibrationView : public View {
Color::black()};
Text label_calibrate{
{16, 5 * 16, 26 * 8, 1 * 16},
{2 * 8, 5 * 16, 26 * 8, 1 * 16},
"Touch targets to calibrate"};
Text label_calibrate_2{
{1 * 8, 6 * 16, 28 * 8, 1 * 16},
"(hold position using stylus)"};
Text label_verify{
{28, 5 * 16, 23 * 8, 1 * 16},
"Touch targets to verify"};

View File

@ -2447,6 +2447,82 @@ static constexpr Bitmap bitmap_icon_camera{
{16, 16},
bitmap_icon_camera_data};
static constexpr uint8_t bitmap_icon_batt_icon_data[] = {
0xC0,
0x03,
0xC0,
0x03,
0xF0,
0x0F,
0x10,
0x08,
0x10,
0x08,
0x10,
0x08,
0x10,
0x08,
0x10,
0x08,
0xF0,
0x0F,
0xF0,
0x0F,
0xF0,
0x0F,
0xF0,
0x0F,
0xF0,
0x0F,
0xF0,
0x0F,
0xF0,
0x0F,
0x00,
0x00,
};
static constexpr Bitmap bitmap_icon_batt_icon{
{16, 16},
bitmap_icon_batt_icon_data};
static constexpr uint8_t bitmap_icon_batt_text_data[] = {
0x00,
0x00,
0x30,
0x06,
0x48,
0x09,
0x48,
0x09,
0x70,
0x0E,
0x40,
0x08,
0x30,
0x06,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x48,
0x00,
0x20,
0x00,
0x10,
0x00,
0x48,
0x00,
0x00,
0x00,
};
static constexpr Bitmap bitmap_icon_batt_text{
{16, 16},
bitmap_icon_batt_text_data};
static constexpr uint8_t bitmap_icon_tools_wipesd_data[] = {
0xF0,
0x3F,

View File

@ -80,6 +80,10 @@ set(EXTCPPSRC
#wardrivemap
external/wardrivemap/main.cpp
external/wardrivemap/ui_wardrivemap.cpp
#tpmsrx
external/tpmsrx/main.cpp
external/tpmsrx/tpms_app.cpp
)
set(EXTAPPLIST
@ -102,4 +106,5 @@ set(EXTAPPLIST
foxhunt_rx
audio_test
wardrivemap
tpmsrx
)

View File

@ -42,6 +42,7 @@ MEMORY
ram_external_app_foxhunt_rx(rwx) : org = 0xADC00000, len = 32k
ram_external_app_audio_test(rwx) : org = 0xADC10000, len = 32k
ram_external_app_wardrivemap(rwx) : org = 0xADC20000, len = 32k
ram_external_app_tpmsrx(rwx) : org = 0xADC30000, len = 32k
}
SECTIONS
@ -160,4 +161,10 @@ SECTIONS
*(*ui*external_app*wardrivemap*);
} > ram_external_app_wardrivemap
.external_app_tpmsrx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_tpmsrx.application_information));
*(*ui*external_app*tpmsrx*);
} > ram_external_app_tpmsrx
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2024 Mark Thompson
*
* 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 "tpms_app.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::tpmsrx {
void initialize_app(ui::NavigationView& nav) {
nav.push<TPMSAppView>();
}
} // namespace ui::external_app::tpmsrx
extern "C" {
__attribute__((section(".external_app.app_tpmsrx.application_information"), used)) application_information_t _application_information_tpmsrx = {
/*.memory_location = */ (uint8_t*)0x00000000, // will be filled at compile time
/*.externalAppEntry = */ ui::external_app::tpmsrx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "TPMS RX",
/*.bitmap_data = */ {
0xC0,
0x03,
0xF0,
0x0F,
0x18,
0x18,
0xEC,
0x37,
0x36,
0x6D,
0x3A,
0x59,
0x4B,
0xD5,
0x8B,
0xD3,
0xCB,
0xD1,
0xAB,
0xD2,
0x9A,
0x5C,
0xB6,
0x6C,
0xEC,
0x37,
0x18,
0x18,
0xF0,
0x0F,
0xC0,
0x03,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::RX,
/*.m4_app_tag = portapack::spi_flash::image_tag_tpms */ {'P', 'T', 'P', 'M'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}

View File

@ -34,15 +34,15 @@ using namespace portapack;
namespace pmem = portapack::persistent_memory;
namespace tpms {
namespace ui::external_app::tpmsrx {
namespace format {
std::string type(Reading::Type type) {
std::string type(tpms::Reading::Type type) {
return to_string_dec_uint(toUType(type), 2);
}
std::string id(TransponderID id) {
std::string id(tpms::TransponderID id) {
return to_string_hex(id.value(), 8);
}
@ -54,17 +54,17 @@ std::string temperature(Temperature temperature) {
return to_string_dec_int(units_fahr ? temperature.fahrenheit() : temperature.celsius(), 3);
}
std::string flags(Flags flags) {
std::string flags(tpms::Flags flags) {
return to_string_hex(flags, 2);
}
static std::string signal_type(SignalType signal_type) {
static std::string signal_type(tpms::SignalType signal_type) {
switch (signal_type) {
case SignalType::FSK_19k2_Schrader:
case tpms::SignalType::FSK_19k2_Schrader:
return "FSK 38400 19200 Schrader";
case SignalType::OOK_8k192_Schrader:
case tpms::SignalType::OOK_8k192_Schrader:
return "OOK - 8192 Schrader";
case SignalType::OOK_8k4_Schrader:
case tpms::SignalType::OOK_8k4_Schrader:
return "OOK - 8400 Schrader";
default:
return "- - - -";
@ -73,15 +73,13 @@ static std::string signal_type(SignalType signal_type) {
} /* namespace format */
} /* namespace tpms */
void TPMSLogger::on_packet(const tpms::Packet& packet, const uint32_t target_frequency) {
const auto hex_formatted = packet.symbols_formatted();
// 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 + " " + tpms::format::signal_type(packet.signal_type()) + " " + hex_formatted.data + "/" + hex_formatted.errors;
std::string entry = target_frequency_str + " " + ui::external_app::tpmsrx::format::signal_type(packet.signal_type()) + " " + hex_formatted.data + "/" + hex_formatted.errors;
log_file.write_entry(packet.received_at(), entry);
}
@ -101,52 +99,9 @@ void TPMSRecentEntry::update(const tpms::Reading& reading) {
}
}
namespace ui {
template <>
void RecentEntriesTable<TPMSRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style) {
std::string line = tpms::format::type(entry.type) + " " + tpms::format::id(entry.id);
if (entry.last_pressure.is_valid()) {
line += " " + tpms::format::pressure(entry.last_pressure.value());
} else {
line +=
" "
" ";
}
if (entry.last_temperature.is_valid()) {
line += " " + tpms::format::temperature(entry.last_temperature.value());
} else {
line +=
" "
" ";
}
if (entry.received_count > 999) {
line += " +++";
} else {
line += " " + to_string_dec_uint(entry.received_count, 3);
}
if (entry.last_flags.is_valid()) {
line += " " + tpms::format::flags(entry.last_flags.value());
} else {
line +=
" "
" ";
}
line.resize(target_rect.width() / 8, ' ');
painter.draw_string(target_rect.location(), style, line);
}
TPMSAppView::TPMSAppView(NavigationView&) {
baseband::run_image(portapack::spi_flash::image_tag_tpms);
// baseband::run_image(portapack::spi_flash::image_tag_tpms);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&rssi,
&field_volume,
@ -167,16 +122,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::units_psi = (bool)i;
format::units_psi = (bool)i;
update_view();
};
options_pressure.set_selected_index(tpms::format::units_psi, true);
options_pressure.set_selected_index(format::units_psi, true);
options_temperature.on_change = [this](size_t, int32_t i) {
tpms::format::units_fahr = (bool)i;
format::units_fahr = (bool)i;
update_view();
};
options_temperature.set_selected_index(tpms::format::units_fahr, true);
options_temperature.set_selected_index(format::units_fahr, true);
logger = std::make_unique<TPMSLogger>();
if (logger) {
@ -234,4 +189,50 @@ void TPMSAppView::on_show_list() {
recent_entries_view.focus();
}
} /* namespace ui */
} // namespace ui::external_app::tpmsrx
namespace ui {
template <>
void RecentEntriesTable<ui::external_app::tpmsrx::TPMSRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style) {
std::string line = ui::external_app::tpmsrx::format::type(entry.type) + " " + ui::external_app::tpmsrx::format::id(entry.id);
if (entry.last_pressure.is_valid()) {
line += " " + ui::external_app::tpmsrx::format::pressure(entry.last_pressure.value());
} else {
line +=
" "
" ";
}
if (entry.last_temperature.is_valid()) {
line += " " + ui::external_app::tpmsrx::format::temperature(entry.last_temperature.value());
} else {
line +=
" "
" ";
}
if (entry.received_count > 999) {
line += " +++";
} else {
line += " " + to_string_dec_uint(entry.received_count, 3);
}
if (entry.last_flags.is_valid()) {
line += " " + ui::external_app::tpmsrx::format::flags(entry.last_flags.value());
} else {
line +=
" "
" ";
}
line.resize(target_rect.width() / 8, ' ');
painter.draw_string(target_rect.location(), style, line);
}
} // namespace ui

View File

@ -38,7 +38,7 @@
#include "tpms_packet.hpp"
namespace tpms {
namespace ui::external_app::tpmsrx {
namespace format {
@ -47,12 +47,6 @@ static bool units_fahr{false};
} /* namespace format */
} /* namespace tpms */
namespace std {
} /* namespace std */
struct TPMSRecentEntry {
using Key = std::pair<tpms::Reading::Type, tpms::TransponderID>;
@ -94,8 +88,6 @@ class TPMSLogger {
LogFile log_file{};
};
namespace ui {
using TPMSRecentEntriesView = RecentEntriesView<TPMSRecentEntries>;
class TPMSAppView : public View {
@ -115,16 +107,18 @@ class TPMSAppView : public View {
private:
RxRadioState radio_state_{
314950000 /* frequency*/,
314950000 /* frequency*/
,
1750000 /* bandwidth */,
2457600 /* sampling rate */};
2457600 /* sampling rate */
};
app_settings::SettingsManager settings_{
"rx_tpms",
app_settings::Mode::RX,
{
{"units_psi"sv, &tpms::format::units_psi},
{"units_fahr"sv, &tpms::format::units_fahr},
{"units_psi"sv, &format::units_psi},
{"units_fahr"sv, &format::units_fahr},
}};
MessageHandlerRegistration message_handler_packet{
@ -198,6 +192,6 @@ class TPMSAppView : public View {
void update_view();
};
} /* namespace ui */
} // namespace ui::external_app::tpmsrx
#endif /*__TPMS_APP_H__*/

View File

@ -72,6 +72,10 @@ File::~File() {
f_close(&f);
}
void File::close() {
f_close(&f);
}
File::Result<File::Size> File::read(void* data, Size bytes_to_read) {
UINT bytes_read = 0;
const auto result = f_read(&f, data, bytes_to_read, &bytes_read);
@ -100,6 +104,10 @@ File::Offset File::tell() const {
return f_tell(&f);
}
File::Result<bool> File::eof() {
return f_eof(&f);
}
File::Result<File::Offset> File::seek(Offset new_position) {
/* NOTE: Returns *old* position, not new position */
const auto old_position = tell();

View File

@ -337,6 +337,7 @@ class File {
// TODO: Return Result<>.
Optional<Error> open(const std::filesystem::path& filename, bool read_only = true, bool create = false);
void close();
Optional<Error> append(const std::filesystem::path& filename);
Optional<Error> create(const std::filesystem::path& filename);
@ -347,6 +348,7 @@ class File {
Result<Offset> seek(uint64_t Offset);
Result<Offset> truncate();
Size size() const;
Result<bool> eof();
template <size_t N>
Result<Size> write(const std::array<uint8_t, N>& data) {

View File

@ -144,6 +144,7 @@ Continuous (Fox-oring)
#include "rffc507x.hpp" /* c/m, avoiding initial short ON Ant_DC_Bias pulse, from cold reset */
rffc507x::RFFC507x first_if;
ui::SystemView* system_view_ptr;
static void event_loop() {
static ui::Context context;
@ -151,6 +152,8 @@ static void event_loop() {
context,
portapack::display.screen_rect()};
system_view_ptr = &system_view;
EventDispatcher event_dispatcher{&system_view, context};
static MessageHandlerRegistration message_handler_display_sleep{
Message::ID::DisplaySleep,

View File

@ -52,6 +52,7 @@ using asahi_kasei::ak4951::AK4951;
#include "sd_card.hpp"
#include "string_format.hpp"
#include "bitmap.hpp"
#include "ui_widget.hpp"
namespace portapack {
@ -585,6 +586,7 @@ init_status_t init() {
chThdSleepMilliseconds(10);
audio::init(portapack_audio_codec());
battery::BatteryManagement::init();
if (lcd_fast_setup)
draw_splash_screen_icon(4, ui::bitmap_icon_speaker);

View File

@ -33,9 +33,12 @@
#include "backlight.hpp"
#include "usb_serial.hpp"
#include "ads1110.hpp"
#include "radio.hpp"
#include "clock_manager.hpp"
#include "temperature_logger.hpp"
#include "battery.hpp"
/* TODO: This would be better as a class to add
* guardrails on setting properties. */

View File

@ -159,6 +159,7 @@ std::string to_string_decimal(float decimal, int8_t precision) {
double fractional_part;
std::string result;
if (precision > 9) precision = 9; // we will convert to uin32_t, and that is the max it can hold.
fractional_part = modf(decimal, &integer_part) * pow(10, precision);
@ -171,6 +172,31 @@ std::string to_string_decimal(float decimal, int8_t precision) {
return result;
}
std::string to_string_decimal_padding(float decimal, int8_t precision, const int32_t l) {
double integer_part;
double fractional_part;
std::string result;
if (precision > 9) precision = 9; // we will convert to uin32_t, and that is the max it can hold.
fractional_part = modf(decimal, &integer_part) * pow(10, precision);
if (fractional_part < 0) {
fractional_part = -fractional_part;
}
result = to_string_dec_int(integer_part) + "." + to_string_dec_uint(fractional_part, precision, '0');
// Add padding with spaces to meet the length requirement
if (result.length() < (uint32_t)l) {
int padding_length = l - result.length();
std::string padding(padding_length, ' ');
result = padding + result;
}
return result;
}
// right-justified frequency in Hz, always 10 characters
std::string to_string_freq(const uint64_t f) {
std::string final_str{""};

View File

@ -54,6 +54,7 @@ std::string to_string_bin(const uint32_t n, const uint8_t l = 0);
std::string to_string_dec_uint(const uint32_t n, const int32_t l, const char fill = ' ');
std::string to_string_dec_int(const int32_t n, const int32_t l, const char fill = 0);
std::string to_string_decimal(float decimal, int8_t precision);
std::string to_string_decimal_padding(float decimal, int8_t precision, const int32_t l);
std::string to_string_hex(uint64_t n, int32_t length);
std::string to_string_hex_array(uint8_t* array, int32_t length);

View File

@ -0,0 +1,222 @@
#include "ui_bmpview.hpp"
#include "portapack.hpp"
bool BMPViewer::load_bmp(const std::filesystem::path& file) {
if (!bmp.open(file, true)) return false;
// calc default zoom level to fit screen, and min / max zoom too
auto rect = screen_rect();
auto d_height = rect.height();
auto d_width = rect.width();
auto b_width = bmp.get_width();
auto b_height = bmp.get_real_height();
// aspects
// if image is smaller then our vp
auto x_w = d_width / b_width;
auto x_h = d_height / b_height;
if (x_w < 1 && x_h < 1) {
// not zoom in, but zoom out
x_w = b_width / d_width;
x_h = b_height / d_height;
x_w = (127 < x_w) ? 127 : x_w;
x_h = (127 < x_h) ? 127 : x_h;
zoom_fit = (x_h > x_w) ? -1 * x_h : -1 * x_w;
} else {
x_w = (127 < x_w) ? 127 : x_w;
x_h = (127 < x_h) ? 127 : x_h;
zoom_fit = (x_h > x_w) ? x_h : x_w;
}
if (zoom_fit > max_zoom) zoom_fit = max_zoom;
min_zoom = zoom_fit - 3;
reset_pos();
return true;
}
bool BMPViewer::move_pos(int32_t delta_x, int32_t delta_y) {
if (!bmp.is_loaded()) return false;
auto rect = screen_rect();
auto d_height = rect.height();
auto d_width = rect.width();
auto ocx = cx; // save old pos
auto ocy = cy;
// top left protection
if (delta_x < 0 && cx <= (uint32_t)(-1 * delta_x))
cx = 0;
else
cx += delta_x;
if (delta_y < 0 && cy <= (uint32_t)(-1 * delta_y))
cy = 0;
else
cy += delta_y;
// right bottom protection
float zt = zoom < 0 ? -1.0f / (float)zoom : (float)zoom;
if (zt == 0) zt = 1;
if (cy + (uint32_t)(d_height / zt) > bmp.get_real_height()) {
cy = (bmp.get_real_height() < (uint32_t)(d_height / zt)) ? 0 : bmp.get_real_height() - (uint32_t)(d_height / zt);
}
if (cx + (uint32_t)(d_width / zt) > bmp.get_width()) {
cx = (bmp.get_width() < (uint32_t)(d_width / zt)) ? 0 : bmp.get_width() - (uint32_t)(d_width / zt);
}
bool ret = !(cx == ocx && ocy == cy); // was any change?
if (ret) set_dirty();
return ret;
}
void BMPViewer::set_zoom(int8_t new_zoom) {
if (!bmp.is_loaded()) return;
if (new_zoom > max_zoom) new_zoom = max_zoom;
if (new_zoom < min_zoom) new_zoom = min_zoom;
if (new_zoom == 0) new_zoom = 1;
if (new_zoom == -1) new_zoom = 1;
zoom = new_zoom;
auto rect = screen_rect();
auto d_height = rect.height();
auto d_width = rect.width();
if (zoom < 0) {
mvx = d_width / 3 * (-1.0 * zoom);
mvy = d_height / 3 * (-1.0 * zoom);
} else {
mvx = d_width / zoom / 3;
mvy = d_height / zoom / 3;
}
move_pos(0, 0); // fix based on zoom, without real move (if not edge case)
set_dirty();
}
// reads a lint from the bmp's bx, by coordinate to the line that's size is cnt. according to zoom
void BMPViewer::get_line(ui::Color* line, uint32_t bx, uint32_t by, uint32_t cnt) {
if (!bmp.is_loaded()) return;
uint32_t last_targetx = 65534;
for (uint32_t x = 0; x < cnt; x++) {
uint32_t targetx = (zoom < 0) ? bx + x * -1 * zoom : bx + x / zoom; // on zoom out could probably avg the pixels, or apply some smoothing, but this is way faster.
if (last_targetx == targetx) {
line[x] = line[x - 1];
continue;
}
last_targetx = targetx;
if (!bmp.seek(targetx, by)) {
line[x] = Color::white(); // can't seek there
} else {
bmp.read_next_px(line[x], false);
}
}
}
void BMPViewer::paint(Painter& painter) {
if (!bmp.is_loaded()) {
painter.draw_string({48, 24}, ui::Styles::white, "Can't load BMP");
return;
}
// get where i can paint
auto rect = screen_rect();
auto d_height = rect.height();
auto d_width = rect.width();
uint32_t by = cy; // we start to read from there
uint32_t last_by = 65534;
ui::Color* line = new ui::Color[d_width];
for (int32_t y = 0; y < d_height; y++) {
by = cy + ((zoom < 0) ? y * -1 * zoom : y / (int32_t)zoom);
if (by != last_by) get_line(line, cx, by, d_width);
last_by = by;
portapack::display.draw_pixels({rect.left(), rect.top() + y, d_width, 1}, line, d_width);
}
delete line;
}
int8_t BMPViewer::get_zoom() {
return zoom;
}
BMPViewer::BMPViewer(Rect parent_rect)
: Widget{parent_rect} {
set_focusable(true);
}
BMPViewer::BMPViewer(Rect parent_rect, const std::filesystem::path& file)
: Widget{parent_rect} {
set_focusable(true);
load_bmp(file);
}
void BMPViewer::on_focus() {
set_highlighted(true);
}
void BMPViewer::on_blur() {
set_dirty();
}
void BMPViewer::reset_pos() {
if (!bmp.is_loaded()) return;
cx = 0;
cy = 0;
set_zoom(zoom_fit);
set_dirty();
}
bool BMPViewer::on_key(const KeyEvent key) {
if (!bmp.is_loaded()) return false;
if (key == KeyEvent::Up) {
return move_pos(0, -1 * mvy);
}
if (key == KeyEvent::Down) {
return move_pos(0, mvy);
}
if (key == KeyEvent::Left) {
return move_pos(-1 * mvx, 0);
}
if (key == KeyEvent::Right) {
return move_pos(mvx, 0);
}
if (key == KeyEvent::Select) {
if (!enter_pass) {
reset_pos();
return true;
}
}
return false;
}
bool BMPViewer::on_encoder(EncoderEvent delta) {
if (!bmp.is_loaded()) return false;
if (delta > 0) {
set_zoom(zoom + delta); // 0 handled in set_zoom
return true;
}
if (delta < 0) {
if (zoom == 1) // not 0, but -1
set_zoom(-2);
else
set_zoom(zoom + delta); // decrease
return true;
}
return false;
}
// sets if the enter key should be passed to parent or handled. true = pass it, false = handle as reset pos+zoom
void BMPViewer::set_enter_pass(bool pass) {
enter_pass = pass;
}
bool BMPViewer::get_enter_pass() {
return enter_pass;
}
bool BMPViewer::on_keyboard(const KeyboardEvent event) {
if (!bmp.is_loaded()) return false;
if (event == '+') {
set_zoom(zoom + 1);
return true;
}
if (event == '-') {
if (zoom == 1) // not 0, but -1
set_zoom(-2);
else
set_zoom(zoom - 1); // decrease
return true;
}
return false;
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UIBMPVIEW_H__
#define __UIBMPVIEW_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "bmpfile.hpp"
#include "ui_styles.hpp"
class BMPViewer : public Widget {
public:
BMPViewer(Rect parent_rect);
BMPViewer(Rect parent_rect, const std::filesystem::path& file);
BMPViewer(const BMPViewer& other) = delete;
BMPViewer& operator=(const BMPViewer& other) = delete;
bool load_bmp(const std::filesystem::path& file);
void paint(Painter& painter) override;
void on_focus() override;
void on_blur() override;
bool on_key(const KeyEvent key) override;
bool on_encoder(EncoderEvent delta) override;
bool on_keyboard(const KeyboardEvent event) override;
void reset_pos();
void set_zoom(int8_t new_zoom);
int8_t get_zoom();
void set_enter_pass(bool pass);
bool get_enter_pass();
private:
void get_line(ui::Color* line, uint32_t bx, uint32_t by, uint32_t cnt);
bool move_pos(int32_t delta_x, int32_t delta_y);
BMPFile bmp{};
int8_t zoom = 1; // positive = zoom in, negative = zoom out 0-invalid 1- no zoom
int8_t zoom_fit = 1; // if this value is set, the image will fit the screen the most
int8_t max_zoom = 10;
int8_t min_zoom = -20; // will be calculated on load
uint32_t cx = 0; // current top-left coordinate
uint32_t cy = 0;
uint32_t mvx = 1; // how much to move on key
uint32_t mvy = 1;
bool enter_pass = false;
};
#endif

View File

@ -84,6 +84,7 @@
#include "ui_weatherstation.hpp"
#include "ui_subghzd.hpp"
#include "ui_whipcalc.hpp"
#include "ui_battinfo.hpp"
#include "ui_external_items_menu_loader.hpp"
// #include "acars_app.hpp"
@ -100,7 +101,7 @@
#include "pocsag_app.hpp"
#include "replay_app.hpp"
#include "soundboard_app.hpp"
#include "tpms_app.hpp"
// #include "tpms_app.hpp" //moved to ext
#include "core_control.hpp"
#include "file.hpp"
@ -171,9 +172,9 @@ const NavigationView::AppList NavigationView::appList = {
{"radiosonde", "Radiosnde", RX, Color::green(), &bitmap_icon_sonde, new ViewFactory<SondeView>()},
{"recon", "Recon", RX, Color::green(), &bitmap_icon_scanner, new ViewFactory<ReconView>()},
{"search", "Search", RX, Color::yellow(), &bitmap_icon_search, new ViewFactory<SearchView>()},
{"tmps", "TPMS Cars", RX, Color::green(), &bitmap_icon_tpms, new ViewFactory<TPMSAppView>()},
{"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory<WeatherView>()},
//{"tmps", "TPMS Cars", RX, Color::green(), &bitmap_icon_tpms, new ViewFactory<TPMSAppView>()},
{"subghzd", "SubGhzD", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<SubGhzDView>()},
{"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory<WeatherView>()},
//{"fskrx", "FSK RX", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory<FskxRxMainView>()},
//{"dmr", "DMR", RX, Color::dark_grey(), &bitmap_icon_dmr, new ViewFactory<NotImplementedView>()},
//{"sigfox", "SIGFOX", RX, Color::dark_grey(), &bitmap_icon_fox, new ViewFactory<NotImplementedView>()},
@ -334,6 +335,9 @@ SystemStatusView::SystemStatusView(
refresh();
};
battery_icon.on_select = [this]() { on_battery_details(); };
battery_text.on_select = [this]() { on_battery_details(); };
button_fake_brightness.on_select = [this](ImageButton&) {
set_dirty();
pmem::toggle_fake_brightness_level();
@ -370,6 +374,26 @@ SystemStatusView::SystemStatusView(
refresh();
}
// when battery icon / text is clicked
void SystemStatusView::on_battery_details() {
if (!nav_.is_valid()) return;
if (batt_info_up) return;
batt_info_up = true;
nav_.push<BattinfoView>();
nav_.set_on_pop([this]() {
batt_info_up = false;
});
}
void SystemStatusView::on_battery_data(const BatteryStateMessage* msg) {
if (!pmem::ui_hide_numeric_battery()) {
battery_text.set_battery(msg->percent, msg->on_charger);
}
if (!pmem::ui_hide_battery_icon()) {
battery_icon.set_battery(msg->percent, msg->on_charger);
};
}
void SystemStatusView::refresh() {
// NB: Order of insertion is the display order Left->Right.
// TODO: Might be better to support hide and only add once.
@ -386,8 +410,20 @@ void SystemStatusView::refresh() {
if (audio::speaker_disable_supported() && !pmem::ui_hide_speaker()) status_icons.add(&toggle_speaker);
if (!pmem::ui_hide_fake_brightness()) status_icons.add(&button_fake_brightness);
if (battery::BatteryManagement::isDetected()) {
uint8_t percent = battery::BatteryManagement::getPercent();
if (!pmem::ui_hide_battery_icon()) {
status_icons.add(&battery_icon);
battery_text.set_battery(percent, false); // got an on select, that may pop up the details of the battery.
};
if (!pmem::ui_hide_numeric_battery()) {
status_icons.add(&battery_text);
battery_text.set_battery(percent, false);
}
}
if (!pmem::ui_hide_sd_card()) status_icons.add(&sd_card_status_view);
status_icons.update_layout();
// Clock status
@ -422,6 +458,10 @@ void SystemStatusView::set_back_enabled(bool new_value) {
}
}
void SystemStatusView::set_back_hidden(bool new_value) {
button_back.hidden(new_value);
}
void SystemStatusView::set_title_image_enabled(bool new_value) {
if (new_value) {
add_child(&button_title);
@ -593,12 +633,13 @@ void SystemStatusView::new_sdcard_structure_checker() {
"following the Wiki.\n"
"Details:\n"
"Mayhem wiki - Updating",
COMPACTYESNO,
YESNO,
[this](bool choice) {
if (choice) {
new_sdcard_structure_worker();
}
});
},
true);
}
}
@ -754,8 +795,9 @@ void NavigationView::display_modal(
const std::string& title,
const std::string& message,
modal_t type,
std::function<void(bool)> on_choice) {
push<ModalMessageView>(title, message, type, on_choice);
std::function<void(bool)> on_choice,
bool compact) {
push<ModalMessageView>(title, message, type, on_choice, compact);
}
void NavigationView::free_view() {
@ -987,6 +1029,9 @@ Context& SystemView::context() const {
NavigationView* SystemView::get_navigation_view() {
return &navigation_view;
}
SystemStatusView* SystemView::get_status_view() {
return &status_view;
}
void SystemView::toggle_overlay() {
static uint8_t last_perf_counter_status = shared_memory.request_m4_performance_counter;
@ -1029,6 +1074,15 @@ void SystemView::paint_overlay() {
}
}
void SystemView::set_app_fullscreen(bool fullscreen) {
auto parent_rect = screen_rect();
Dim status_view_height = (fullscreen) ? 0 : 16;
status_view.hidden(fullscreen);
navigation_view.set_parent_rect(
{{0, status_view_height},
{parent_rect.width(), static_cast<Dim>(parent_rect.height() - status_view_height)}});
}
/* ***********************************************************************/
void BMPView::focus() {
@ -1072,11 +1126,13 @@ ModalMessageView::ModalMessageView(
const std::string& title,
const std::string& message,
modal_t type,
std::function<void(bool)> on_choice)
std::function<void(bool)> on_choice,
bool compact)
: title_{title},
message_{message},
type_{type},
on_choice_{on_choice} {
on_choice_{on_choice},
compact{compact} {
if (type == INFO) {
add_child(&button_ok);
button_ok.on_select = [this, &nav](Button&) {
@ -1084,7 +1140,7 @@ ModalMessageView::ModalMessageView(
nav.pop();
};
} else if (type == YESNO || type == COMPACTYESNO) {
} else if (type == YESNO) {
add_children({&button_yes,
&button_no});
@ -1109,22 +1165,22 @@ ModalMessageView::ModalMessageView(
}
void ModalMessageView::paint(Painter& painter) {
if (type_ != COMPACTYESNO) {
portapack::display.drawBMP({100, 48}, modal_warning_bmp, false);
}
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, false);
// Break lines.
auto lines = split_string(message_, '\n');
for (size_t i = 0; i < lines.size(); ++i) {
painter.draw_string(
{1 * 8, (Coord)(((type_ == COMPACTYESNO) ? 8 * 3 : 120) + (i * 16))},
{1 * 8, (Coord)(((compact) ? 8 * 3 : 120) + (i * 16))},
style(),
lines[i]);
}
}
void ModalMessageView::focus() {
if ((type_ == YESNO) || (type_ == COMPACTYESNO)) {
if (type_ == YESNO) {
button_no.focus(); // it should default to NO like all the operating system
} else {
button_ok.focus();

View File

@ -59,7 +59,6 @@ namespace ui {
enum modal_t {
INFO = 0,
YESNO,
COMPACTYESNO,
ABORT
};
@ -121,7 +120,8 @@ class NavigationView : public View {
const std::string& title,
const std::string& message,
modal_t type,
std::function<void(bool)> on_choice = nullptr);
std::function<void(bool)> on_choice = nullptr,
bool compact = false);
void focus() override;
@ -186,13 +186,14 @@ class SystemStatusView : public View {
SystemStatusView(NavigationView& nav);
void set_back_enabled(bool new_value);
void set_back_hidden(bool new_value);
void set_title_image_enabled(bool new_value);
void set_title(const std::string new_value);
private:
bool already_shown_sdcard_warning_in_this_boot{false};
static constexpr auto default_title = "";
bool batt_info_up = false; // to prevent show multiple batt info dialog
NavigationView& nav_;
Rectangle backdrop{
@ -279,6 +280,9 @@ class SystemStatusView : public View {
SDCardStatusView sd_card_status_view{
{0, 0 * 16, 2 * 8, 1 * 16}};
BatteryTextField battery_text{{0, 0, 2 * 8, 1 * 16}, 102};
BatteryIcon battery_icon{{0, 0, 10, 1 * 16}, 102};
void on_converter();
void on_bias_tee();
void on_camera();
@ -286,15 +290,26 @@ class SystemStatusView : public View {
void refresh();
void on_clk();
void rtc_battery_workaround();
void new_sdcard_structure_checker();
void new_sdcard_structure_worker();
void on_battery_data(const BatteryStateMessage* msg);
void on_battery_details();
MessageHandlerRegistration message_handler_refresh{
Message::ID::StatusRefresh,
[this](const Message* const p) {
(void)p;
this->refresh();
}};
MessageHandlerRegistration message_handler_battery{
Message::ID::BatteryStateData,
[this](const Message* const p) {
const auto message = static_cast<const BatteryStateMessage*>(p);
this->on_battery_data(message);
}};
};
class InformationView : public View {
@ -380,8 +395,10 @@ class SystemView : public View {
Context& context() const override;
void toggle_overlay();
void paint_overlay();
void set_app_fullscreen(bool fullscreen);
NavigationView* get_navigation_view();
SystemStatusView* get_status_view();
private:
uint8_t overlay_active{0};
@ -419,7 +436,8 @@ class ModalMessageView : public View {
const std::string& title,
const std::string& message,
modal_t type,
std::function<void(bool)> on_choice);
std::function<void(bool)> on_choice,
bool compact = false);
void paint(Painter& painter) override;
void focus() override;
@ -431,6 +449,7 @@ class ModalMessageView : public View {
const std::string message_;
const modal_t type_;
const std::function<void(bool)> on_choice_;
const bool compact;
Button button_ok{
{0, screen_height - 8 * 8, screen_width, 48},

View File

@ -0,0 +1,134 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyleft (ɔ) 2024 zxkmm with the GPL license
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "usb_serial_asyncmsg.hpp"
/// value
// to_string_bin/ to_string_decimal/ to_string_hex/ to_string_hex_array/ to_string_dec_uint/ to_string_dec_int etc seems usellss so i didn't add them here
// usage: UsbSerialAsyncmsg::asyncmsg(num);
template <>
void UsbSerialAsyncmsg::asyncmsg<int64_t>(const int64_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<int32_t>(const int32_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<int16_t>(const int16_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<int8_t>(const int8_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<uint8_t>(const uint8_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<uint16_t>(const uint16_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<uint32_t>(const uint32_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<uint64_t>(const uint64_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<float>(const float& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_decimal(data, 7).c_str());
}
/// fs things
template <>
// usage: UsbSerialAsyncmsg::asyncmsg(path);
void UsbSerialAsyncmsg::asyncmsg<std::filesystem::path>(const std::filesystem::path& data) {
if (!portapack::async_tx_enabled) {
return;
}
std::string path_str = data.string();
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", path_str.c_str());
}
/// string
// string obj
template <>
// usage: UsbSerialAsyncmsg::asyncmsg(str);
void UsbSerialAsyncmsg::asyncmsg<std::string>(const std::string& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", data.c_str());
}
// string literal AKA char[]
// usage: UsbSerialAsyncmsg::asyncmsg("abc");
void UsbSerialAsyncmsg::asyncmsg(const char* data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", data);
}

View File

@ -2,6 +2,7 @@
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyleft (ɔ) 2024 zxkmm with the GPL license
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
@ -27,7 +28,11 @@
#include <vector>
#include <string>
#include <sstream>
#include <chprintf.h>
#include "portapack.hpp"
#include "ch.h"
#include "chprintf.h"
#include "hal.h"
#include "usb_serial_device_to_host.h"
class UsbSerialAsyncmsg {
@ -37,6 +42,8 @@ class UsbSerialAsyncmsg {
template <typename VECTORCOVER>
static void asyncmsg(const std::vector<VECTORCOVER>& data);
static void asyncmsg(const char* data); // string literal
};
/*Notes:
@ -45,100 +52,11 @@ class UsbSerialAsyncmsg {
* - so delete all debug things before you push your code to production
* - use this client to filter only PP devices: https://github.com/zxkmm/Pyserial-Demo-portapack
* - usage:
* #include "usb_serial_debug_bridge.hpp"
* portapack::async_tx_enabled = true; // note that use this when debugging, unless the msg would be forbidden. but don't use this in production, since it's not real async and multiple serial transmittions will broken each other. if this class is used in other scene in the future, just use command to cover (protect your serial tramsnitton) in your extern thing: asyncmsg enable --- your cmd --- asyncmsg disable
* #include "usb_serial_asyncmsg.hpp"
* UsbSerialAsyncmsg::asyncmsg("Hello PP");
* */
/// value
// to_string_bin/ to_string_decimal/ to_string_hex/ to_string_hex_array/ to_string_dec_uint/ to_string_dec_int etc seems usellss so i didn't add them here
template <>
// usage: UsbSerialAsyncmsg::asyncmsg(num);
void UsbSerialAsyncmsg::asyncmsg<int64_t>(const int64_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<int32_t>(const int32_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<int16_t>(const int16_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<int8_t>(const int8_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<uint8_t>(const uint8_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<uint16_t>(const uint16_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<uint32_t>(const uint32_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
template <>
void UsbSerialAsyncmsg::asyncmsg<uint64_t>(const uint64_t& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
}
/// fs things
template <>
// usage: UsbSerialAsyncmsg::asyncmsg(path);
void UsbSerialAsyncmsg::asyncmsg<std::filesystem::path>(const std::filesystem::path& data) {
if (!portapack::async_tx_enabled) {
return;
}
std::string path_str = data.string();
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", path_str.c_str());
}
/// string
template <>
// usage: UsbSerialAsyncmsg::asyncmsg(str);
void UsbSerialAsyncmsg::asyncmsg<std::string>(const std::string& data) {
if (!portapack::async_tx_enabled) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", data.c_str());
}
/// vec worker
// ussgae: UsbSerialAsyncmsg::asyncmsg(vec);
template <typename VECTORCOVER>

View File

@ -498,12 +498,6 @@ set(MODE_CPPSRC
)
DeclareTargets(PTON tones)
### TPMS
set(MODE_CPPSRC
proc_tpms.cpp
)
DeclareTargets(PTPM tpms)
### Wideband Spectrum
@ -651,6 +645,15 @@ set(MODE_CPPSRC
)
DeclareTargets(PABP audio_beep)
### TPMS
set(MODE_CPPSRC
proc_tpms.cpp
)
DeclareTargets(PTPM tpms)
### HackRF "factory" firmware
add_custom_command(

133
firmware/common/ads1110.cpp Normal file
View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2024 jLynx.
*
* 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 "ads1110.hpp"
#include "utility.hpp"
#include <algorithm>
#include <cstdint>
namespace battery {
namespace ads1110 {
constexpr uint16_t BATTERY_MIN_VOLTAGE = 3000;
constexpr uint16_t BATTERY_MAX_VOLTAGE = 4000;
void ADS1110::init() {
if (!detected_) {
detected_ = detect();
}
if (detected_) {
// Set the configuration register
write(0x8C);
}
}
bool ADS1110::detect() {
uint8_t data[3];
if (bus.receive(bus_address, data, 3)) {
// Check if the received data is valid
uint8_t configRegister = data[2];
if ((configRegister & 0x0F) == 0x0C) {
// The configuration register value matches the expected value (0x8C)
detected_ = true;
return true;
}
}
detected_ = false;
return false;
}
bool ADS1110::write(const uint8_t value) {
return bus.transmit(bus_address, &value, 1);
}
// returns the batt voltage in mV
uint16_t ADS1110::readVoltage() {
// Read the conversion result
uint8_t data[3];
if (!bus.receive(bus_address, data, 3)) {
return 0.0f; // Return 0 if the read fails
}
uint16_t raw = (static_cast<uint16_t>(data[0]) << 8) | data[1];
// Calculate the voltage based on the output code
int16_t voltage = 0;
float minCode = 0;
float pga = 0.0f;
uint8_t pga_rate = data[2] & 0x03;
switch (pga_rate) {
case 0:
pga = 1.0f;
break;
case 1:
pga = 2.0f;
break;
case 2:
pga = 4.0f;
break;
case 3:
pga = 8.0f;
break;
default:
// Handle invalid data rate
break;
}
uint8_t data_rate = (data[2] >> 2) & 0x03;
switch (data_rate) {
case 0: // 240
minCode = -2048.0;
break;
case 1: // 60
minCode = -8192.0;
break;
case 2: // 30
minCode = -16384.0;
break;
case 3: // 15
minCode = -32768.0;
break;
default:
// Handle invalid data rate
break;
}
// 2.048 is the reference voltage & 2.0 is to make up for the voltage divider
voltage = (int16_t)(raw / (-1.0 * minCode) * pga * 2.048 * 2.0 * 1000.0); // v to mV
if (voltage < 0) voltage *= -1; // should not happen in this build, but prevent it
return (uint16_t)voltage;
}
void ADS1110::getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage) {
voltage = readVoltage();
// Calculate the remaining battery percentage
batteryPercentage = (float)(voltage - BATTERY_MIN_VOLTAGE) / (float)(BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE) * 100.0;
// Limit the values to the valid range
batteryPercentage = (batteryPercentage > 100) ? 100 : batteryPercentage;
// ToDo: if its > 4, then 100%, if < 3 then 0%
}
} /* namespace ads1110 */
} // namespace battery

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2024 jLynx.
*
* 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 __ADS1110_H__
#define __ADS1110_H__
#include <cstdint>
#include <array>
#include <string>
#include "i2c_pp.hpp"
namespace battery {
namespace ads1110 {
using address_t = uint8_t;
class ADS1110 {
public:
constexpr ADS1110(I2C& bus, const I2C::address_t bus_address)
: bus(bus), bus_address(bus_address), detected_(false) {}
void init();
bool detect();
bool isDetected() const { return detected_; }
uint16_t readVoltage();
void getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage);
private:
I2C& bus;
const I2C::address_t bus_address;
bool detected_;
bool write(const uint8_t value);
};
} /* namespace ads1110 */
} // namespace battery
#endif /* __ADS1110_H__ */

111
firmware/common/battery.cpp Normal file
View File

@ -0,0 +1,111 @@
#include "battery.hpp"
#include "event_m0.hpp"
#include "portapack.hpp"
#include "ads1110.hpp"
// uncomment if you want to emulate batt management system
// #define USE_BATT_EMULATOR
extern I2C portapack::i2c0;
namespace battery {
constexpr uint32_t BATTERY_UPDATE_INTERVAL = 30000;
BatteryManagement::BatteryModules BatteryManagement::detected_ = BatteryManagement::BATT_NONE;
ads1110::ADS1110 battery_ads1110{portapack::i2c0, 0x48};
Thread* BatteryManagement::thread = nullptr;
void BatteryManagement::init() {
// try to detect supported modules
detected_ = BATT_NONE;
if (battery_ads1110.detect()) {
battery_ads1110.init();
detected_ = BATT_ADS1110;
}
// add new supported module detect + init here
#ifdef USE_BATT_EMULATOR
if (detected_ == BATT_NONE) {
detected_ = BATT_EMULATOR;
}
#endif
if (detected_ != BATT_NONE) {
// sets timer to query and broadcats this info
create_thread();
}
}
// sets the values, it the currend module supports it.
bool BatteryManagement::getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current, bool& isCharging) {
if (detected_ == BATT_NONE) return false;
if (detected_ == BATT_ADS1110) {
battery_ads1110.getBatteryInfo(batteryPercentage, voltage);
return true;
}
// add new module query here
#ifdef USE_BATT_EMULATOR
if (detected_ == BATT_EMULATOR) {
batteryPercentage += 5; // %
if (batteryPercentage > 100) batteryPercentage = 0;
voltage = rand() % 1000 + 3000; // mV
current = rand() % 150; // mA
isCharging = rand() % 2;
return true;
}
#endif
(void)isCharging; // keep the compiler calm
(void)current;
return false;
}
uint8_t BatteryManagement::getPercent() {
if (detected_ == BATT_NONE) return 102;
uint8_t batteryPercentage = 0;
bool isCharging = false;
uint16_t voltage = 0;
int32_t current = 0;
getBatteryInfo(batteryPercentage, voltage, current, isCharging);
return batteryPercentage;
}
uint16_t BatteryManagement::getVoltage() {
if (detected_ == BATT_NONE) return 0;
if (detected_ == BATT_NONE) return 102;
uint8_t batteryPercentage = 0;
bool isCharging = false;
uint16_t voltage = 0;
int32_t current = 0;
getBatteryInfo(batteryPercentage, voltage, current, isCharging);
return voltage;
}
msg_t BatteryManagement::timer_fn(void* arg) {
(void)arg;
if (!detected_) return 0;
uint8_t batteryPercentage = 102;
bool isCharging = false;
uint16_t voltage = 0;
int32_t current = 0;
chThdSleepMilliseconds(1000); // wait ui for fully load
while (1) {
if (BatteryManagement::getBatteryInfo(batteryPercentage, voltage, current, isCharging)) {
// send local message
BatteryStateMessage msg{batteryPercentage, isCharging, voltage};
EventDispatcher::send_message(msg);
}
chThdSleepMilliseconds(BATTERY_UPDATE_INTERVAL);
}
return 0;
}
void BatteryManagement::create_thread() {
thread = chThdCreateFromHeap(NULL, 512, NORMALPRIO, BatteryManagement::timer_fn, nullptr);
}
} // namespace battery

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2024 HTotoo.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __BATTERY_H__
#define __BATTERY_H__
#include <cstdint>
#include "ch.h"
namespace battery {
class BatteryManagement {
public:
enum BatteryModules {
BATT_NONE = 0,
BATT_ADS1110 = 1,
BATT_EMULATOR = 254
};
static void init();
static bool isDetected() { return detected_ != BATT_NONE; }
static bool getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current, bool& isCharging);
static uint16_t getVoltage();
static uint8_t getPercent();
private:
static void create_thread();
static msg_t timer_fn(void* arg);
static Thread* thread;
static BatteryModules detected_; // if there is any batt management system
};
}; // namespace battery
#endif

View File

@ -29,7 +29,7 @@ struct bmp_header_t {
uint32_t image_data;
uint32_t BIH_size;
uint32_t width;
uint32_t height;
int32_t height; // can be negative, to signal the bottom-up or reserve status
uint16_t planes;
uint16_t bpp;
uint32_t compression;

285
firmware/common/bmpfile.cpp Normal file
View File

@ -0,0 +1,285 @@
/*
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "bmpfile.hpp"
bool BMPFile::is_loaded() {
return is_opened;
}
// fix height info
uint32_t BMPFile::get_real_height() {
if (!is_opened) return 0;
return bmp_header.height >= 0 ? (uint32_t)bmp_header.height : (uint32_t)(-1 * bmp_header.height);
}
// get bmp width
uint32_t BMPFile::get_width() {
if (!is_opened) return 0;
return bmp_header.width;
}
// get if the rows are bottom up (for most bmp), or up to bottom (negative height, we use it for write)
bool BMPFile::is_bottomup() {
return (bmp_header.height >= 0);
}
BMPFile::~BMPFile() {
close();
}
// closes file
void BMPFile::close() {
is_opened = false;
bmpimage.close();
}
// creates a new bmp file. for now, hardcoded to 3 byte colour depth
bool BMPFile::create(const std::filesystem::path& file, uint32_t x, uint32_t y) {
is_opened = false;
is_read_ony = true;
bmpimage.close(); // if already open, close before open a new
if (file_exists(file)) {
delete_file(file); // overwrite
}
auto result = bmpimage.open(file, false, true);
if (!result.value().ok()) return false;
file_pos = 0;
byte_per_row = (x * 3 % 4 == 0) ? x * 3 : (x * 3 + (4 - ((x * 3) % 4))); // with padding
bmpimage.seek(file_pos);
bmp_header.signature = 0x4D42;
bmp_header.planes = 1;
bmp_header.compression = 0;
bmp_header.bpp = 24; // 3 byte depth
bmp_header.width = x;
bmp_header.height = 0; // for now, will expand
bmp_header.image_data = 0x36;
bmp_header.BIH_size = 0x28;
bmp_header.h_res = 100;
bmp_header.v_res = 100;
byte_per_px = 3;
type = 1;
bmp_header.size = sizeof(bmp_header) + get_real_height() * byte_per_row; // with padding! --will update later with expand
bmp_header.data_size = bmp_header.size - sizeof(bmp_header_t);
bmp_header.colors_count = 0;
bmp_header.icolors_count = 0;
bmpimage.write(&bmp_header, sizeof(bmp_header_t));
file_pos = bmp_header.image_data;
is_opened = true;
is_read_ony = false;
if (!expand_y(y)) return false; // will fill with 0, and update header data
seek(0, 0);
return true;
}
// opens the file and parses header data. return true on success
bool BMPFile::open(const std::filesystem::path& file, bool readonly) {
is_opened = false;
is_read_ony = true;
bmpimage.close(); // if already open, close before open a new
auto result = bmpimage.open(file, readonly, false);
if (!result.value().ok()) return false;
file_pos = 0;
bmpimage.seek(file_pos);
auto read_size = bmpimage.read(&bmp_header, sizeof(bmp_header_t));
if (!((bmp_header.signature == 0x4D42) && // "BM" Signature
(bmp_header.planes == 1) && // Seems always to be 1
(bmp_header.compression == 0 || bmp_header.compression == 3))) { // No compression
return false;
}
char buffer[257];
switch (bmp_header.bpp) {
case 16:
file_pos = 0x36;
memset(buffer, 0, 16);
bmpimage.read(buffer, 16);
byte_per_px = 2;
if (buffer[1] == 0x7C)
type = 3; // A1R5G5B5
else
type = 0; // R5G6B5
break;
case 24:
type = 1;
byte_per_px = 3;
break;
case 32:
type = 2;
byte_per_px = 4;
break;
default:
// not supported
return false;
break;
}
byte_per_row = (bmp_header.width * byte_per_px % 4 == 0) ? bmp_header.width * byte_per_px : (bmp_header.width * byte_per_px + (4 - ((bmp_header.width * byte_per_px) % 4)));
file_pos = bmp_header.image_data;
is_opened = true;
is_read_ony = readonly;
currx = 0;
curry = 0;
return true;
}
// jumps to next pixel. false on the end
bool BMPFile::advance_curr_px(uint32_t num = 1) {
if (curry >= get_real_height()) return false;
uint32_t rowsToAdvance = (currx + num) / bmp_header.width;
uint32_t nx = (currx + num) % bmp_header.width;
uint32_t ny = curry + rowsToAdvance;
if (ny >= get_real_height()) {
return false;
}
seek(nx, ny);
return true;
}
// reads next px, then advance the pos (and seek). return false on error
bool BMPFile::read_next_px(ui::Color& px, bool seek = true) {
if (!is_opened) return false;
uint8_t buffer[4];
auto res = bmpimage.read(buffer, byte_per_px);
if (res.is_error()) return false;
switch (type) {
case 0: // R5G6B5
case 3: // A1R5G5B5
if (!type)
px = ui::Color((uint16_t)buffer[0] | ((uint16_t)buffer[1] << 8));
else
px = ui::Color(((uint16_t)buffer[0] & 0x1F) | ((uint16_t)buffer[0] & 0xE0) << 1 | ((uint16_t)buffer[1] & 0x7F) << 9);
break;
case 1: // 24
default:
px = ui::Color(buffer[2], buffer[1], buffer[0]);
break;
case 2: // 32
px = ui::Color(buffer[2], buffer[1], buffer[0]);
break;
}
if (seek) advance_curr_px();
return true;
}
// if you set this, then the expanded part (or the newly created) will be filled with this color. but the expansion or the creation will be slower.
void BMPFile::set_bg_color(ui::Color background) {
bg = background;
use_bg = true;
}
// delete bg color. default. creation or expansion will be fast, but the file will contain random garbage. no problem if you write all pixels later.
void BMPFile::delete_db_color() {
use_bg = false;
}
// writes a color data to the current position, and advances 1 px. true on success, false on error
bool BMPFile::write_next_px(ui::Color& px) {
if (!is_opened) return false;
if (is_read_ony) return false;
uint8_t buffer[4];
switch (type) {
case 0: // R5G6B5
case 3: // A1R5G5B5
if (!type) {
buffer[0] = (px.r() << 3) | (px.g() >> 3); // todo test in future
buffer[1] = (px.g() << 5) | px.b();
} else {
buffer[0] = (1 << 7) | (px.r() << 2) | (px.g() >> 3); // todo test in future
buffer[1] = (px.g() << 5) | px.b();
}
break;
case 1: // 24
default:
buffer[2] = px.r();
buffer[1] = px.g();
buffer[0] = px.b();
break;
case 2: // 32
buffer[2] = px.r();
buffer[1] = px.g();
buffer[0] = px.b();
buffer[3] = 255;
break;
}
auto res = bmpimage.write(buffer, byte_per_px);
if (res.is_error()) return false;
advance_curr_px();
return true;
}
// positions in the file to the given pixel. 0 based indexing
bool BMPFile::seek(uint32_t x, uint32_t y) {
if (!is_opened) return false;
if (x >= bmp_header.width) return false;
if (y >= get_real_height()) return false;
if (!BMPFile::is_bottomup()) {
file_pos = bmp_header.image_data; // nav to start pos.
file_pos += y * byte_per_row;
file_pos += x * byte_per_px;
bmpimage.seek(file_pos);
currx = x;
curry = y;
} else {
file_pos = bmp_header.image_data; // nav to start pos.
file_pos += (get_real_height() - y - 1) * byte_per_row;
file_pos += x * byte_per_px;
bmpimage.seek(file_pos);
currx = x;
curry = y;
}
return true;
}
// expands the image with a delta (y). also seek's t it's begining. in bottumup format, it should be used carefully!
bool BMPFile::expand_y_delta(uint32_t delta_y) {
return expand_y(get_real_height() + delta_y);
}
// expands the image to a new y size. also seek's t it's begining. in bottumup format, it should be used carefully!
bool BMPFile::expand_y(uint32_t new_y) {
if (!is_opened) return false; // not yet opened
uint32_t old_height = get_real_height();
if (new_y < old_height) return true; // already bigger
if (is_read_ony) return false; // can't expand
uint32_t delta = (new_y - old_height) * byte_per_row;
bmp_header.size += delta;
bmp_header.data_size += delta;
bmp_header.height = -1 * new_y; //-1*, so no bottom-up structure needed. easier to expand.
bmpimage.seek(0);
bmpimage.write(&bmp_header, sizeof(bmp_header)); // overwrite header
bmpimage.seek(bmp_header.size); // seek to new end to expand
// fill with bg color if needed
if (use_bg) {
seek(0, old_height); // to the new begin
size_t newpxcount = ((new_y - old_height) * bmp_header.width);
for (size_t i = 0; i < newpxcount; ++i)
write_next_px(bg);
}
if (is_bottomup()) {
seek(0, new_y - old_height); // seek to the new chunk begin
} else {
seek(0, curry + 1); // seek to the begin of the new chunk
}
return true;
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __BMPFILE__H
#define __BMPFILE__H
#include <cstring>
#include <string>
#include "file.hpp"
#include "bmp.hpp"
#include "ui.hpp"
class BMPFile {
public:
~BMPFile();
bool open(const std::filesystem::path& file, bool readonly);
bool create(const std::filesystem::path& file, uint32_t x, uint32_t y);
void close();
bool is_loaded();
bool seek(uint32_t x, uint32_t y);
bool expand_y(uint32_t new_y);
bool expand_y_delta(uint32_t delta_y);
uint32_t getbpr() { return byte_per_row; };
bool read_next_px(ui::Color& px, bool seek);
bool write_next_px(ui::Color& px);
uint32_t get_real_height();
uint32_t get_width();
bool is_bottomup();
void set_bg_color(ui::Color background);
void delete_db_color();
private:
bool advance_curr_px(uint32_t num);
bool is_opened = false;
bool is_read_ony = true;
File bmpimage{};
size_t file_pos = 0;
bmp_header_t bmp_header{};
uint8_t type = 0;
uint8_t byte_per_px = 1;
uint32_t byte_per_row = 0;
uint32_t currx = 0;
uint32_t curry = 0;
ui::Color bg{};
bool use_bg = false;
};
#endif

View File

@ -123,6 +123,7 @@ class Message {
EnvironmentData = 65,
AudioBeep = 66,
PocsagTosend = 67,
BatteryStateData = 68,
MAX
};
@ -1409,4 +1410,20 @@ class PocsagTosendMessage : public Message {
uint64_t addr = 0;
};
class BatteryStateMessage : public Message {
public:
constexpr BatteryStateMessage(
uint8_t percent,
bool on_charger,
uint16_t voltage)
: Message{ID::BatteryStateData},
percent{percent},
on_charger{on_charger},
voltage{voltage} {
}
uint8_t percent = 0;
bool on_charger = false;
uint16_t voltage = 0; // mV
};
#endif /*__MESSAGE_H__*/

View File

@ -132,8 +132,8 @@ struct ui_config2_t {
bool hide_mute : 1;
bool hide_fake_brightness : 1;
bool UNUSED_1 : 1;
bool UNUSED_2 : 1;
bool hide_numeric_battery : 1;
bool hide_battery_icon : 1;
bool UNUSED_3 : 1;
bool UNUSED_4 : 1;
bool UNUSED_5 : 1;
@ -423,6 +423,7 @@ void defaults() {
set_encoder_dial_sensitivity(DIAL_SENSITIVITY_NORMAL);
set_config_speaker_disable(true); // Disable AK4951 speaker by default (in case of OpenSourceSDRLab H2)
set_menu_color(Color::grey());
set_ui_hide_numeric_battery(true); // hide the numeric battery by default - no space to display it
// Default values for recon app.
set_recon_autosave_freqs(false);
@ -954,6 +955,13 @@ bool ui_hide_fake_brightness() {
return data->ui_config2.hide_fake_brightness;
}
bool ui_hide_numeric_battery() {
return data->ui_config2.hide_numeric_battery;
}
bool ui_hide_battery_icon() {
return data->ui_config2.hide_battery_icon;
}
void set_ui_hide_speaker(bool v) {
data->ui_config2.hide_speaker = v;
}
@ -986,6 +994,12 @@ void set_ui_hide_sd_card(bool v) {
void set_ui_hide_fake_brightness(bool v) {
data->ui_config2.hide_fake_brightness = v;
}
void set_ui_hide_numeric_battery(bool v) {
data->ui_config2.hide_numeric_battery = v;
}
void set_ui_hide_battery_icon(bool v) {
data->ui_config2.hide_battery_icon = v;
}
/* Converter */
bool config_converter() {
@ -1248,6 +1262,8 @@ bool debug_dump() {
pmem_dump_file.write_line("ui_config2 hide_sd_card: " + to_string_dec_uint(data->ui_config2.hide_sd_card));
pmem_dump_file.write_line("ui_config2 hide_mute: " + to_string_dec_uint(data->ui_config2.hide_mute));
pmem_dump_file.write_line("ui_config2 hide_fake_brightness: " + to_string_dec_uint(data->ui_config2.hide_fake_brightness));
pmem_dump_file.write_line("ui_config2 hide_battery_icon: " + to_string_dec_uint(data->ui_config2.hide_battery_icon));
pmem_dump_file.write_line("ui_config2 hide_numeric_battery: " + to_string_dec_uint(data->ui_config2.hide_numeric_battery));
// misc_config bits
pmem_dump_file.write_line("misc_config config_audio_mute: " + to_string_dec_int(config_audio_mute()));

View File

@ -335,6 +335,8 @@ bool ui_hide_sleep();
bool ui_hide_bias_tee();
bool ui_hide_clock();
bool ui_hide_fake_brightness();
bool ui_hide_numeric_battery();
bool ui_hide_battery_icon();
bool ui_hide_sd_card();
void set_ui_hide_speaker(bool v);
void set_ui_hide_mute(bool v);
@ -345,6 +347,8 @@ void set_ui_hide_sleep(bool v);
void set_ui_hide_bias_tee(bool v);
void set_ui_hide_clock(bool v);
void set_ui_hide_fake_brightness(bool v);
void set_ui_hide_numeric_battery(bool v);
void set_ui_hide_battery_icon(bool v);
void set_ui_hide_sd_card(bool v);
// sd persisting settings

View File

@ -2044,6 +2044,135 @@ bool TextField::on_touch(TouchEvent event) {
return false;
}
/* BatteryIcon *************************************************************/
BatteryIcon::BatteryIcon(Rect parent_rect, uint8_t percent)
: Widget(parent_rect) {
this->set_battery(percent, false);
set_focusable(true);
}
void BatteryIcon::getAccessibilityText(std::string& result) {
result = to_string_dec_uint(percent_) + "%";
}
void BatteryIcon::getWidgetName(std::string& result) {
result = "Battery percent";
}
void BatteryIcon::set_battery(uint8_t percentage, bool charge) {
if (charge == charge_ && percent_ == percentage) return;
percent_ = percentage;
charge_ = charge;
set_dirty();
}
bool BatteryIcon::on_key(KeyEvent key) {
if (key == KeyEvent::Select && on_select) {
on_select();
return true;
}
return false;
}
bool BatteryIcon::on_touch(TouchEvent event) {
if (event.type == TouchEvent::Type::Start) {
focus();
return true;
}
if (event.type == TouchEvent::Type::End && on_select) {
on_select();
return true;
}
return false;
}
void BatteryIcon::paint(Painter& painter) {
ui::Rect rect = screen_rect(); // 10, 1 * 16
painter.fill_rectangle(rect, has_focus() || highlighted() ? Color::light_grey() : Color::dark_grey()); // clear
ui::Color battColor = (charge_) ? Color::cyan() : Color::green();
// batt body:
painter.draw_vline({rect.left() + 1, rect.top() + 2}, rect.height() - 4, battColor);
painter.draw_vline({rect.right() - 2, rect.top() + 2}, rect.height() - 4, battColor);
painter.draw_hline({rect.left() + 1, rect.top() + 2}, rect.width() - 2, battColor);
painter.draw_hline({rect.left() + 1, rect.bottom() - 2}, rect.width() - 2, battColor);
// batt cap:
painter.draw_hline({rect.left() + 3, rect.top() + 1}, rect.width() - 6, battColor);
painter.draw_hline({rect.left() + 3, 0}, rect.width() - 6, battColor);
if (percent_ > 100) { // error / unk
painter.draw_string({rect.left() + 2, rect.top() + 3}, font::fixed_5x8, Color::white(), Color::dark_grey(), "?");
return;
}
int8_t ppx = (rect.bottom() - 3) - (rect.top() + 2); // 11px max height to draw bars
int8_t ptd = (int8_t)((static_cast<float>(percent_) / 100.0f) * (float)ppx + 0.5); // pixels to draw
int8_t pp = ppx - ptd; // pixels to start from
if (percent_ >= 70)
battColor = Color::green();
else if (percent_ >= 40)
battColor = Color::orange();
else
battColor = Color::red();
// fill the bars
for (int y = pp; y < ppx; y++) {
painter.draw_hline({rect.left() + 2, rect.top() + 3 + y}, rect.width() - 4, battColor);
}
}
/* BatteryTextField *************************************************************/
BatteryTextField::BatteryTextField(Rect parent_rect, uint8_t percent)
: Widget(parent_rect) {
this->set_battery(percent, false);
set_focusable(true);
}
void BatteryTextField::paint(Painter& painter) {
Color bg = has_focus() || highlighted() ? Color::light_grey() : Color::dark_grey();
ui::Rect rect = screen_rect(); // 2 * 8, 1 * 16
painter.fill_rectangle(rect, bg); // clear
std::string txt_batt = percent_ <= 100 ? to_string_dec_uint(percent_) : "UNK";
int xdelta = 0;
if (txt_batt.length() == 1)
xdelta = 5;
else if (txt_batt.length() == 2)
xdelta = 2;
painter.draw_string({rect.left() + xdelta, rect.top()}, font::fixed_5x8, Color::white(), bg, txt_batt);
painter.draw_string({rect.left(), rect.top() + 8}, font::fixed_5x8, Color::white(), bg, (charge_) ? "+%" : " %");
}
void BatteryTextField::getAccessibilityText(std::string& result) {
result = to_string_dec_uint(percent_) + "%";
}
void BatteryTextField::getWidgetName(std::string& result) {
result = "Battery percent";
}
void BatteryTextField::set_battery(uint8_t percentage, bool charge) {
if (charge == charge_ && percent_ == percentage) return;
charge_ = charge;
percent_ = percentage;
set_dirty();
}
bool BatteryTextField::on_key(KeyEvent key) {
if (key == KeyEvent::Select && on_select) {
on_select();
return true;
}
return false;
}
bool BatteryTextField::on_touch(TouchEvent event) {
if (event.type == TouchEvent::Type::Start) {
focus();
return true;
}
if (event.type == TouchEvent::Type::End && on_select) {
on_select();
return true;
}
return false;
}
/* NumberField ***********************************************************/
NumberField::NumberField(

View File

@ -33,6 +33,8 @@
#include "portapack.hpp"
#include "utility.hpp"
#include "ui/ui_font_fixed_5x8.hpp"
#include <functional>
#include <memory>
#include <string>
@ -781,6 +783,52 @@ class TextField : public Text {
using Text::set;
};
class BatteryTextField : public Widget {
public:
std::function<void()> on_select{};
BatteryTextField(Rect parent_rect, uint8_t percent);
void paint(Painter& painter) override;
void set_battery(uint8_t percentage, bool charge);
void set_text(std::string_view value);
bool on_key(KeyEvent key) override;
bool on_touch(TouchEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
uint8_t percent_{102};
bool charge_{false};
static constexpr Style style{
.font = font::fixed_5x8,
.background = Color::dark_grey(),
.foreground = Color::white(),
};
};
class BatteryIcon : public Widget {
public:
std::function<void()> on_select{};
BatteryIcon(Rect parent_rect, uint8_t percent);
void paint(Painter& painter) override;
void set_battery(uint8_t percentage, bool charge);
bool on_key(KeyEvent key) override;
bool on_touch(TouchEvent event) override;
void getAccessibilityText(std::string& result) override;
void getWidgetName(std::string& result) override;
private:
uint8_t percent_{102};
bool charge_{false};
};
class NumberField : public Widget {
public:
std::function<void(NumberField&)> on_select{};

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

View File

@ -1,3 +1,7 @@
#
# created by zxkmm on ArchHaseeHome - 2024/01/xx
#
from PIL import Image
import numpy as np

View File

@ -0,0 +1,41 @@
#
# Created by zxkmm on ArchHaseeHome - 2024/04/22
#
import subprocess
import sys
if_succeed = False
def get_gcc_version(elf_file):
global if_succeed
output = subprocess.check_output(['readelf', '-p', '.comment', elf_file])
output = output.decode('utf-8')
lines = output.split('\n')
for line in lines:
if 'GCC:' in line:
version_info = line.split('GCC:')[1].strip()
if_succeed = True
return version_info
if not if_succeed: # didn't use try except here cuz don't need to break compile if this is bad result anyway
return None
if __name__ == "__main__":
if len(sys.argv) != 2:
current_dir = subprocess.check_output(['pwd'])
print(f"current dir: {current_dir}")
print("usage python check_gcc_version_from_elf.py xxx.elf")
sys.exit(1)
elf_file = sys.argv[1]
version_info = get_gcc_version(elf_file)
if version_info is not None:
print(f"real gcc version readed from elf: {version_info}")
else:
print("something went wrong when checking gcc version don't worry tho, it's not deadly issue. skip.")

View File

@ -3,6 +3,7 @@
#
# Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
# Copyright (C) 2024 Mark Thompson
# Copyleft (ɔ) 2024 zxkmm with the GPL license
#
# This file is part of PortaPack.
#
@ -23,9 +24,11 @@
#
import sys
import os
from external_app_info import maximum_application_size
from external_app_info import external_apps_address_start
from external_app_info import external_apps_address_end
import subprocess
usage_message = """
PortaPack SPI flash image generator
@ -34,20 +37,59 @@ Usage: <command> <application_path> <baseband_path> <output_path>
Where paths refer to the .bin files for each component project.
"""
def read_image(path):
f = open(path, 'rb')
data = f.read()
f.close()
return data
f = open(path, 'rb')
data = f.read()
f.close()
return data
def write_image(data, path):
f = open(path, 'wb')
f.write(data)
f.close()
f = open(path, 'wb')
f.write(data)
f.close()
def get_gcc_version_from_elf(elf_file):
succeed = False
output = subprocess.check_output(['readelf', '-p', '.comment', elf_file])
output = output.decode('utf-8')
lines = output.split('\n')
for line in lines:
if 'GCC:' in line:
version_info = line.split('GCC:')[1].strip()
succeed = True
return version_info
if not succeed: # didn't use try except here cuz don't need to break compile if this is bad result anyway
return None
def get_gcc_version_from_elf_files_in_giving_path_or_filename_s_path(path):
elf_files = []
if os.path.isdir(path):
elf_files = [os.path.join(path, f) for f in os.listdir(path) if f.endswith(".elf")]
elif os.path.isfile(path):
elf_files = [os.path.join(os.path.dirname(path), f) for f in os.listdir(os.path.dirname(path)) if
f.endswith(".elf")]
else:
print("gave path or filename is not valid") # didn't use except nor exit here cuz don't need to break compile if this is bad result anyway
gcc_versions = []
for elf_file in elf_files:
version_info = get_gcc_version_from_elf(elf_file)
if version_info is not None:
extract_elf_file_name = os.path.basename(elf_file)
gcc_versions.append("gcc version of " + extract_elf_file_name + " is " + version_info)
return gcc_versions
if len(sys.argv) != 4:
print(usage_message)
sys.exit(-1)
print(usage_message)
sys.exit(-1)
application_image = read_image(sys.argv[1])
baseband_image = read_image(sys.argv[2])
@ -56,43 +98,47 @@ output_path = sys.argv[3]
spi_size = 1048576
images = (
{
'name': 'application',
'data': application_image,
'size': len(application_image),
},
{
'name': 'baseband',
'data': baseband_image,
'size': len(baseband_image),
},
{
'name': 'application',
'data': application_image,
'size': len(application_image),
},
{
'name': 'baseband',
'data': baseband_image,
'size': len(baseband_image),
},
)
spi_image = bytearray()
spi_image_default_byte = bytearray((255,))
for image in images:
if len(image['data']) > image['size']:
raise RuntimeError('data for image "%(name)s" is longer than 0x%(size)x bytes 0x%(sz)x' % {'name':image['name'], 'size':image['size'], 'sz':len(image['data'])})
pad_size = image['size'] - len(image['data'])
padded_data = image['data'] + (spi_image_default_byte * pad_size)
spi_image += padded_data
if len(image['data']) > image['size']:
raise RuntimeError(
'data for image "%(name)s" is longer than 0x%(size)x bytes 0x%(sz)x' % {'name': image['name'],
'size': image['size'],
'sz': len(image['data'])})
pad_size = image['size'] - len(image['data'])
padded_data = image['data'] + (spi_image_default_byte * pad_size)
spi_image += padded_data
if len(spi_image) > spi_size - 4:
raise RuntimeError('SPI flash image size of %d exceeds device size of %d bytes' % (len(spi_image) + 4, spi_size))
raise RuntimeError('SPI flash image size of %d exceeds device size of %d bytes' % (len(spi_image) + 4, spi_size))
pad_size = spi_size - 4 - len(spi_image)
for i in range(pad_size):
spi_image += spi_image_default_byte
spi_image += spi_image_default_byte
# quick "add up the words" checksum, and check for possible references to code in external apps
checksum = 0
for i in range(0, len(spi_image), 4):
snippet = spi_image[i:i+4]
val = int.from_bytes(snippet, byteorder='little')
checksum += val
if (val >= external_apps_address_start) and (val < external_apps_address_end) and ((val & 0xFFFF) < maximum_application_size):
print ("WARNING: Possible external code address", hex(val), "at offset", hex(i), "in", sys.argv[3])
snippet = spi_image[i:i + 4]
val = int.from_bytes(snippet, byteorder='little')
checksum += val
if (val >= external_apps_address_start) and (val < external_apps_address_end) and (
(val & 0xFFFF) < maximum_application_size):
print("WARNING: Possible external code address", hex(val), "at offset", hex(i), "in", sys.argv[3])
final_checksum = 0
checksum = (final_checksum - checksum) & 0xFFFFFFFF
@ -102,4 +148,14 @@ spi_image += checksum.to_bytes(4, 'little')
write_image(spi_image, output_path)
percent_remaining = round(1000 * pad_size / spi_size) / 10;
print ("Space remaining in flash ROM:", pad_size, "bytes (", percent_remaining, "%)")
print("Space remaining in flash ROM:", pad_size, "bytes (", percent_remaining, "%)")
print("check gcc versions from all elf target")
application_gcc_versions = get_gcc_version_from_elf_files_in_giving_path_or_filename_s_path(sys.argv[1])
baseband_gcc_versions = get_gcc_version_from_elf_files_in_giving_path_or_filename_s_path(sys.argv[2])
for itap in application_gcc_versions:
print(itap)
for itbb in baseband_gcc_versions:
print(itbb)

View File

@ -12,3 +12,4 @@ a=433050000,b=434790000,d=ISM 433
a=868000000,b=868200000,d=ISM 868
a=1574920000,b=1575920000,d=GPS L1
a=1226600000,b=1228600000,d=GPS L2
a=1175450000,b=1177450000,d=GPS L5