mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2024-12-04 23:45:26 +00:00
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
This commit is contained in:
parent
f572b00391
commit
f6a2d21624
@ -177,6 +177,7 @@ set(CPPSRC
|
||||
${COMMON}/utility.cpp
|
||||
${COMMON}/wm8731.cpp
|
||||
${COMMON}/ads1110.cpp
|
||||
${COMMON}/battery.cpp
|
||||
${COMMON}/performance_counter.cpp
|
||||
${COMMON}/bmpfile.cpp
|
||||
app_settings.cpp
|
||||
@ -283,6 +284,7 @@ set(CPPSRC
|
||||
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
|
||||
|
104
firmware/application/apps/ui_battinfo.cpp
Normal file
104
firmware/application/apps/ui_battinfo.cpp
Normal 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
|
83
firmware/application/apps/ui_battinfo.hpp
Normal file
83
firmware/application/apps/ui_battinfo.hpp
Normal 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__*/
|
@ -42,7 +42,7 @@ DfuMenu::DfuMenu(NavigationView& nav)
|
||||
&text_info_line_9,
|
||||
&text_info_line_10});
|
||||
|
||||
if (portapack::battery_ads1110.isDetected()) {
|
||||
if (battery::BatteryManagement::isDetected()) {
|
||||
add_child(&voltage_label);
|
||||
add_child(&text_info_line_11);
|
||||
}
|
||||
@ -53,7 +53,7 @@ void DfuMenu::paint(Painter& painter) {
|
||||
size_t m0_fragmented_free_space = 0;
|
||||
const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space);
|
||||
|
||||
auto lines = (portapack::battery_ads1110.isDetected() ? 11 : 10) + 2;
|
||||
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));
|
||||
@ -65,8 +65,8 @@ 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 (portapack::battery_ads1110.isDetected()) {
|
||||
text_info_line_11.set(to_string_decimal_padding(portapack::battery_ads1110.readVoltage(), 3, 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;
|
||||
|
@ -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);
|
||||
|
@ -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"};
|
||||
|
@ -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,
|
||||
|
@ -85,7 +85,6 @@ ClockManager clock_manager{
|
||||
|
||||
WM8731 audio_codec_wm8731{i2c0, 0x1a};
|
||||
AK4951 audio_codec_ak4951{i2c0, 0x12};
|
||||
ads1110::ADS1110 battery_ads1110{i2c0, 0x48};
|
||||
|
||||
ReceiverModel receiver_model;
|
||||
TransmitterModel transmitter_model;
|
||||
@ -587,7 +586,7 @@ init_status_t init() {
|
||||
chThdSleepMilliseconds(10);
|
||||
|
||||
audio::init(portapack_audio_codec());
|
||||
battery_ads1110.init();
|
||||
battery::BatteryManagement::init();
|
||||
|
||||
if (lcd_fast_setup)
|
||||
draw_splash_screen_icon(4, ui::bitmap_icon_speaker);
|
||||
|
@ -38,6 +38,7 @@
|
||||
#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. */
|
||||
@ -63,7 +64,6 @@ extern portapack::USBSerial usb_serial;
|
||||
extern si5351::Si5351 clock_generator;
|
||||
extern ClockManager clock_manager;
|
||||
|
||||
extern ads1110::ADS1110 battery_ads1110;
|
||||
extern ReceiverModel receiver_model;
|
||||
extern TransmitterModel transmitter_model;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include "ui_bmpview.hpp"
|
||||
#include "usb_serial_asyncmsg.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
bool BMPViewer::load_bmp(const std::filesystem::path& 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"
|
||||
@ -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
|
||||
@ -667,8 +703,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() {
|
||||
@ -997,11 +1034,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&) {
|
||||
@ -1034,13 +1073,13 @@ ModalMessageView::ModalMessageView(
|
||||
}
|
||||
|
||||
void ModalMessageView::paint(Painter& painter) {
|
||||
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)(120 + (i * 16))},
|
||||
{1 * 8, (Coord)(((compact) ? 8 * 3 : 120) + (i * 16))},
|
||||
style(),
|
||||
lines[i]);
|
||||
}
|
||||
|
@ -120,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;
|
||||
|
||||
@ -191,7 +192,7 @@ class SystemStatusView : public View {
|
||||
|
||||
private:
|
||||
static constexpr auto default_title = "";
|
||||
|
||||
bool batt_info_up = false; // to prevent show multiple batt info dialog
|
||||
NavigationView& nav_;
|
||||
|
||||
Rectangle backdrop{
|
||||
@ -278,6 +279,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();
|
||||
@ -285,6 +289,8 @@ class SystemStatusView : public View {
|
||||
void refresh();
|
||||
void on_clk();
|
||||
void rtc_battery_workaround();
|
||||
void on_battery_data(const BatteryStateMessage* msg);
|
||||
void on_battery_details();
|
||||
|
||||
MessageHandlerRegistration message_handler_refresh{
|
||||
Message::ID::StatusRefresh,
|
||||
@ -292,6 +298,13 @@ class SystemStatusView : public View {
|
||||
(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 {
|
||||
@ -418,7 +431,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;
|
||||
@ -430,6 +444,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{
|
||||
{10 * 8, 14 * 16, 10 * 8, 48},
|
||||
|
@ -24,10 +24,11 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
|
||||
namespace battery {
|
||||
namespace ads1110 {
|
||||
|
||||
constexpr float BATTERY_MIN_VOLTAGE = 3.0;
|
||||
constexpr float BATTERY_MAX_VOLTAGE = 4.0;
|
||||
constexpr uint16_t BATTERY_MIN_VOLTAGE = 3000;
|
||||
constexpr uint16_t BATTERY_MAX_VOLTAGE = 4000;
|
||||
|
||||
void ADS1110::init() {
|
||||
if (!detected_) {
|
||||
@ -50,6 +51,7 @@ bool ADS1110::detect() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
detected_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -57,7 +59,8 @@ bool ADS1110::write(const uint8_t value) {
|
||||
return bus.transmit(bus_address, &value, 1);
|
||||
}
|
||||
|
||||
float ADS1110::readVoltage() {
|
||||
// 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)) {
|
||||
@ -67,7 +70,7 @@ float ADS1110::readVoltage() {
|
||||
uint16_t raw = (static_cast<uint16_t>(data[0]) << 8) | data[1];
|
||||
|
||||
// Calculate the voltage based on the output code
|
||||
float voltage = 0.0f;
|
||||
int16_t voltage = 0;
|
||||
float minCode = 0;
|
||||
float pga = 0.0f;
|
||||
|
||||
@ -110,21 +113,21 @@ float ADS1110::readVoltage() {
|
||||
}
|
||||
|
||||
// 2.048 is the reference voltage & 2.0 is to make up for the voltage divider
|
||||
voltage = raw / (-1.0 * minCode) * pga * 2.048 * 2.0;
|
||||
|
||||
return voltage;
|
||||
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(float& batteryPercentage, float& voltage) {
|
||||
void ADS1110::getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage) {
|
||||
voltage = readVoltage();
|
||||
|
||||
// Calculate the remaining battery percentage
|
||||
batteryPercentage = (voltage - BATTERY_MIN_VOLTAGE) /
|
||||
(BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE) * 100.0;
|
||||
batteryPercentage = (float)(voltage - BATTERY_MIN_VOLTAGE) / (float)(BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE) * 100.0;
|
||||
|
||||
// Limit the values to the valid range
|
||||
batteryPercentage = std::clamp(batteryPercentage, 0.0f, 100.0f);
|
||||
batteryPercentage = (batteryPercentage > 100) ? 100 : batteryPercentage;
|
||||
// ToDo: if its > 4, then 100%, if < 3 then 0%
|
||||
}
|
||||
|
||||
} /* namespace ads1110 */
|
||||
} // namespace battery
|
@ -27,11 +27,10 @@
|
||||
#include <string>
|
||||
|
||||
#include "i2c_pp.hpp"
|
||||
|
||||
namespace battery {
|
||||
namespace ads1110 {
|
||||
|
||||
using address_t = uint8_t;
|
||||
using reg_t = uint16_t;
|
||||
|
||||
class ADS1110 {
|
||||
public:
|
||||
@ -39,10 +38,11 @@ class ADS1110 {
|
||||
: bus(bus), bus_address(bus_address), detected_(false) {}
|
||||
|
||||
void init();
|
||||
bool detect();
|
||||
bool isDetected() const { return detected_; }
|
||||
|
||||
float readVoltage();
|
||||
void getBatteryInfo(float& batteryPercentage, float& voltage);
|
||||
uint16_t readVoltage();
|
||||
void getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage);
|
||||
|
||||
private:
|
||||
I2C& bus;
|
||||
@ -50,9 +50,8 @@ class ADS1110 {
|
||||
bool detected_;
|
||||
|
||||
bool write(const uint8_t value);
|
||||
bool detect();
|
||||
};
|
||||
|
||||
} /* namespace ads1110 */
|
||||
|
||||
} // namespace battery
|
||||
#endif /* __ADS1110_H__ */
|
111
firmware/common/battery.cpp
Normal file
111
firmware/common/battery.cpp
Normal 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
|
50
firmware/common/battery.hpp
Normal file
50
firmware/common/battery.hpp
Normal 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
|
@ -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__*/
|
||||
|
@ -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()));
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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{};
|
||||
|
BIN
firmware/graphics/icon_batt_icon.png
Normal file
BIN
firmware/graphics/icon_batt_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 167 B |
BIN
firmware/graphics/icon_batt_text.png
Normal file
BIN
firmware/graphics/icon_batt_text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 188 B |
Loading…
Reference in New Issue
Block a user