Compare commits

...

36 Commits

Author SHA1 Message Date
Totoo 99d8fbdff0 Added the proposed new geotagging from flipper files. kept the old ones for compatibility (#2289) 2024-10-08 12:26:08 +08:00
Totoo 09c2c43be0 acars to ext but disabled (#2288) 2024-10-07 15:58:37 +02:00
Totoo b9771b2350 I2c fix (#2287)
* Resolve rename

* battery has warpers
2024-10-07 11:55:11 +02:00
Totoo 83b65ba6ce I2C device manager (#2282)
* message on dev list change

* dc detect

* added sht3x sensor.

* separete environment data from light

* max17055 moved to i2c dev

* sht fix, goterror detection fix

* fix ext sensor app display for a lot of devices.

* added bh1750 driver

* autoscan on main view

* added devlist mutex

* better timing

* fix h2 sw8 on poweron by usb
2024-10-06 22:14:27 +02:00
sommermorgentraum d4edb5f5f9 setting&autostart&widgets (OptionField and Waveform) imp (#2286)
* test

* test

* format

* format

* tune order
2024-10-06 21:58:03 +02:00
Erwin Ried 536d25db64 Update README.md (#2284)
Previous link was reported as ilegitimate store
2024-10-05 18:38:13 +02:00
Erwin Ried eca1db26e4 New sn0ren video about H4M (#2283) 2024-10-05 11:19:22 +02:00
sommermorgentraum 7d96bc4efe Optional sha512 to spread out the final password for rand_pw app (#2281)
* fix issue from #1444

* add hash sha512

* check point 1, with useless init pw usage

* gui

* should be works

* fix warning from sah512 lib

* delete diff file for debugging
2024-10-05 11:19:11 +02:00
Erwin Ried b0c92d9baf Update README.md (#2280)
aliexpress fixed the links for the alternative portapacks (the non opensourcesdrlabs)
2024-10-03 13:43:00 +02:00
Erwin Ried c1af935022 Update README.md (#2279)
Link to OpenSourceSDRLab store
2024-10-03 07:22:21 +02:00
sommermorgentraum e75fb59169 fix issue from #1444 (#2278) 2024-10-02 16:17:40 +02:00
sommermorgentraum 2d1314a3fa fine tune pwd gen app layout (#2277)
* init

* format
2024-10-01 16:16:59 +08:00
Totoo a398ed1634 Random app imp (#2276) 2024-09-30 17:45:13 +02:00
zxkmm 105742acbc add random ext app (#2273)
* copy paste from afsk

* add generate thing

* todo: remove uneeded code

* todo: remove uneeded code

* todo: asycnmsg detect indicate, check way to not use global password

* removed audio

* add log warn modal

* small tune

* remove drunk code

* password var global

* seed as text instead of console

* remove console

* should be almost done

* naming fix

* bitmap now moved to seperate folder, that header i deleted isn't related to firmware

* get cmake format back

* get cmake format back - try2

* try to revert cmake file

* get cmake format back - try3

* get cmake format back - try4

* move to util

* disable amp when launch

* refactor name

* cmake fix

* try to revert cmake file

* init in methods local var

* user another methods to generate

* change pause to flood

* fix log

* fine tune

* clang format

* fix name
2024-09-30 22:13:55 +08:00
zxkmm b43eaa8786 protect serial asyncmsg with indicate to prevent halt (#2272) 2024-09-29 10:19:29 +02:00
gullradriel b19ed5fec5 Looking glass narrow view marker wrap around (#2271)
* allow 1 MHz wide view (because why not?)
* allow marker to wrap around each corner
* added another define for clarity
2024-09-29 01:33:09 +08:00
zxkmm 419bc75d2f add ccache option and transparent color instead of bool (#2269)
* add ccache option and use language helper for proto view app

* add transparent color

* typo

* fix my typo

* fix my typo 2
2024-09-28 14:50:37 +02:00
Totoo b3a0ad018c Limit 50 items per page in sndboard. Maybe solves oom (#2263) 2024-09-28 14:48:32 +02:00
Totoo a21e9ca1af Nav fix, fixes #1197 (#2270)
* Nav fix, fixes #1197
2024-09-26 14:29:35 +02:00
Totoo 20f45e88f3 fix protoview shift, and add comments to languagehelper (#2268) 2024-09-26 09:22:24 +08:00
zxkmm 967506fb97 add pause feat for protoView app (#2267) 2024-09-25 17:45:45 +02:00
zxkmm 24ab2f29fa touch to pop splash (#2260)
* touch to pop

* debug

* have to add another var

* works but since touch screen is laggy, it's tricky

* tune to single touch

* remove debug things

* safer with sleep

* textual

* @HTotoo resolved the sleep issue

* use pop handler in another place (button)
2024-09-23 20:57:15 +02:00
jLynx 572776bb9e Update past_version.txt 2024-09-23 17:17:47 +12:00
jLynx 073c4ed9f2 Update version.txt 2024-09-23 17:17:38 +12:00
zxkmm 42233e62df transparent splash logo & fix tab widget color glitch (#2256) 2024-09-22 19:40:54 +02:00
zxkmm 9390317a75 add shift feat for protcol view tool (#2258)
* add shift

* add shift

* format

* add padding (wrote by AI)

* layout fine tune

* remove debug thing

* edit per request

* remove torjan (jk)

* int 16 should be good enough for it
2024-09-22 15:30:31 +02:00
Erwin Ried 89b51095bf New H4M version (#2259) 2024-09-21 16:15:17 +02:00
Totoo 9438c9a574 SSTV TX to ext (#2254)
* SSTV TX to ext
2024-09-20 21:41:44 +08:00
jLynx 7feef9d38b Learn ic fix (#2253)
* WIP

* Fixed merge

* Added test code

* WIP

* Clean up

* add reset learned params

* ui fix

* ui fix2

* Updated func

* Fixed english

* WIP

* WIP testing

* Added new debug app

* Got new app for debug

* Got new app for debug

* Got one full page showing

* Got app working with all reg

* Got app working with all reg

* Got full hex showing

* Fixed dp

* Fixed dp

* Moved entities

* Enabled apps again

* SHow battery debug if ic

* WIP

* Refactored further

* WIP

* Refactor and clean up

* Refactor and clean up

* fix warning, add tte/ttf, add cycles counter.

* wip

* morse tx to ext app

* fix morse crash

* fix ui

* Updated wording

* WIP

* WIP

* Updated to display hours and minutes

---------

Co-authored-by: HTotoo <ttotoo@gmail.com>
2024-09-20 09:59:17 +00:00
zxkmm 582bb02a75 add bitmap arr to file (or reverse) converter (#2248)
* add covert tool

* fix for generator

* fix for generator
2024-09-20 09:56:33 +02:00
Totoo bfa7f3ca6b Wardrive map improv (#2251)
Added paginator.
Added Flipper SUB file support.
2024-09-16 21:59:28 +02:00
Mark Thompson 21facd9cc3 Options to select 314.9 or 315MHz TPMS freq (#2252) 2024-09-15 23:20:04 -05:00
zxkmm d30234aaa6 reword of #2158 (#2249) 2024-09-11 19:11:11 +02:00
gullradriel d45a17236e USB Serial setfreq support (#2246) 2024-09-09 20:01:25 +02:00
zxkmm 281e52d143 fine tune the invert logic (#2243) 2024-09-08 17:38:40 +02:00
Totoo 1f78646054 only inv or fakebrightness (#2240) 2024-09-08 09:35:09 +02:00
98 changed files with 4818 additions and 1917 deletions
+1 -1
View File
@@ -1 +1 @@
v2.0.0
v2.0.1
+1 -1
View File
@@ -1 +1 @@
v2.0.1
v2.0.2
+20 -7
View File
@@ -23,6 +23,8 @@ cmake_policy(SET CMP0005 NEW)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex-m.cmake)
option(USE_CCACHE "Enable ccache, please keep an eye on this because sometimes it's not quite stable and generated unusable binary" OFF)
project(portapack-h1)
set(EXPECTED_GCC_VERSION "9.2.1")
@@ -72,13 +74,24 @@ set(HACKRF_CPLD_XSVF_PATH ${HACKRF_PATH}/firmware/cpld/sgpio_if/${HACKRF_CPLD_XS
set(HACKRF_FIRMWARE_DFU_IMAGE ${hackrf_usb_BINARY_DIR}/${HACKRF_FIRMWARE_DFU_FILENAME})
set(HACKRF_FIRMWARE_BIN_IMAGE ${hackrf_usb_BINARY_DIR}/${HACKRF_FIRMWARE_BIN_FILENAME})
# this seems causing some issues (ref. in discord discussions), so temporarily disabled, until figure out the pros and cons and bugs of this tool.
#find_program(CCACHE "ccache")
#if(CCACHE)
# set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE})
# set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE})
# set(ENV{CCACHE_SLOPPINESS} pch_defines,time_macros)
#endif(CCACHE)
# this seems causing some issues (ref. in discord discussions), but it sometimes do helps, so you have your choice to use. it's default disabled, unless you use such command:
# cmake -DUSE_CCACHE=ON ..
# `make` or `make -j` or `make -j10` etc
# the default behavior `cmake ..` isn't changed (aka don't use ccache)
message(STATUS "-------v ccache info--------")
if(USE_CCACHE)
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_FOUND})
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_FOUND})
message(STATUS "Using ccache, please keep an eye on this because sometimes it's not quite stable and generated unusable binary.\n-- use `cmake -DUSE_CCACHE=OFF ..` to turn off ccache")
else()
message(WARNING "ccache is enabled but not found on the system.")
endif()
else()
message(STATUS "Not using ccache, use `cmake -DUSE_CCACHE=ON ..` to turn on ccache")
endif()
message(STATUS "-------^ ccache info--------")
enable_testing()
add_subdirectory(firmware)
+6 -6
View File
@@ -11,15 +11,15 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
[<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_front.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview) [<img src="https://raw.githubusercontent.com/wiki/portapack-mayhem/mayhem-firmware/img/hw_overview_h2_inside.png" height="400">](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview#portapack-internals)
*[PortaPack H2+HackRF+battery](https://grabify.link/7T28JP) (clone) with a custom [3d printed case](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure)*
*[PortaPack H2+HackRF+battery](https://grabify.link/ZYYPLB) (clone) with a custom [3d printed case](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure)*
# What is this?
If you are new to *HackRF+PortaPack+Mayhem*, check these:
[<img alt="Its TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="700">](https://grabify.link/X4D5TF)
[<img alt="The new HackRF Portapack H4M" src="https://img.youtube.com/vi/onQRdCITmuk/maxresdefault.jpg" width="701">](https://grabify.link/0JUHZ6)
[<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="230">](https://grabify.link/9UZGEW) [<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="230">](https://grabify.link/5MU2VH) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="230">](https://grabify.link/C0J6ZR)
[<img alt="Its TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="172">](https://grabify.link/X4D5TF) [<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="172">](https://grabify.link/9UZGEW) [<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="172">](https://grabify.link/5MU2VH) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="172">](https://grabify.link/C0J6ZR)
# Frequently Asked Questions
@@ -27,11 +27,11 @@ This repository expands upon the previous work by many people and aims to consta
## What to buy?
:heavy_check_mark: A recommended one is this [PortaPack H2](https://grabify.link/7T28JP), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure).
:heavy_check_mark: ![Static Badge](https://img.shields.io/badge/NEW-yellow) The fabulous [PortaPack H4M Mayhem](https://grabify.link/VPMPSL), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://grabify.link/99SAMT). Join their giveaways on discord (check the badge on top).
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://grabify.link/HTDXG5), but its advantages remain debated. Share your insights on our [wiki](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview).
:heavy_check_mark: A recommended one is this [PortaPack H2](https://grabify.link/ZYYPLB), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/H2-Enclosure).
:heavy_check_mark: Our friends at OpenSourceSDRLab give away five units every three months in our discord (check the badge on top) of one of their [PortaPack H2 bundle](https://grabify.link/T328XL) or [only metal case kits](https://grabify.link/LG0OUY), you can support them too by ordering.
:heavy_check_mark: Some individuals lean towards the [H2 with a metal enclosure](https://grabify.link/XNGTAP), but its advantages remain debated. Share your insights on our [wiki](https://github.com/portapack-mayhem/mayhem-firmware/wiki/Hardware-overview).
:warning: Be cautious , *ask* the seller about compatibility with the latest releases. Look out for the description of the item, if they provide the firmware files for an older version or they have custom setup instructions, this means it might be **NOT compatible**, for example:
+9 -6
View File
@@ -125,12 +125,14 @@ set(CSRC
${CHIBIOS}/os/various/chprintf.c
)
#look for all i2cdev_ files
file(GLOB I2CDEV_SOURCES ${COMMON}/i2cdev_*.cpp)
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
set(CPPSRC
main.cpp
shell.cpp
${COMMON}/acars_packet.cpp
${COMMON}/adsb.cpp
${COMMON}/adsb_frame.cpp
${COMMON}/ais_baseband.cpp
@@ -176,8 +178,8 @@ set(CPPSRC
${COMMON}/ui_language.cpp
${COMMON}/utility.cpp
${COMMON}/wm8731.cpp
${COMMON}/ads1110.cpp
${COMMON}/max17055.cpp
${COMMON}/i2cdevmanager.cpp
${I2CDEV_SOURCES}
${COMMON}/battery.cpp
${COMMON}/performance_counter.cpp
${COMMON}/bmpfile.cpp
@@ -206,6 +208,7 @@ set(CPPSRC
irq_rtc.cpp
log_file.cpp
metadata_file.cpp
flipper_subfile.cpp
portapack.cpp
usb_serial_shell.cpp
usb_serial_shell_filesystem.cpp
@@ -265,7 +268,6 @@ set(CPPSRC
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
# apps/analog_tv_app.cpp
@@ -291,6 +293,7 @@ set(CPPSRC
apps/ui_btle_rx.cpp
# apps/ui_coasterp.cpp
apps/ui_debug.cpp
apps/ui_debug_max17055.cpp
apps/ui_dfu_menu.cpp
apps/ui_encoders.cpp
apps/ui_fileman.cpp
@@ -305,7 +308,7 @@ set(CPPSRC
apps/ui_looking_glass_app.cpp
apps/ui_mictx.cpp
apps/ui_modemsetup.cpp
apps/ui_morse.cpp
# apps/ui_morse.cpp
# apps/ui_nrf_rx.cpp
# apps/ui_nuoptix.cpp
apps/ui_playlist.cpp
@@ -325,7 +328,7 @@ set(CPPSRC
# apps/ui_spectrum_painter_text.cpp
# apps/ui_spectrum_painter.cpp
apps/ui_ss_viewer.cpp
apps/ui_sstvtx.cpp
# apps/ui_sstvtx.cpp #moved to ext
apps/ui_standalone_view.cpp
apps/ui_subghzd.cpp
# apps/ui_test.cpp
+5 -3
View File
@@ -35,6 +35,8 @@ using namespace portapack;
namespace ui {
#define FILE_PER_PAGE 50
bool SoundBoardView::is_active() const {
return (bool)replay_thread;
}
@@ -178,9 +180,9 @@ void SoundBoardView::refresh_list() {
if ((reader->channels() == 1) && ((reader->bits_per_sample() == 8) || (reader->bits_per_sample() == 16))) {
// sounds[c].ms_duration = reader->ms_duration();
// sounds[c].path = u"WAV/" + entry.path().native();
if (count >= (page - 1) * 100 && count < page * 100) {
if (count >= (page - 1) * FILE_PER_PAGE && count < page * FILE_PER_PAGE) {
file_list.push_back(entry.path());
if (file_list.size() == 100) {
if (file_list.size() == FILE_PER_PAGE) {
page++;
break;
}
@@ -225,7 +227,7 @@ void SoundBoardView::refresh_list() {
menu_view.set_highlighted(0); // Refresh
}
if (file_list.size() < 100) {
if (file_list.size() < FILE_PER_PAGE) {
page = 1;
}
}
+50 -2
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
@@ -49,7 +50,10 @@ void BattinfoView::update_result() {
text_voltage.set("UNKNOWN");
text_current.set("-");
text_charge.set("-");
text_cycles.set("-");
text_ttef.set("-");
text_method.set("-");
text_warn.set("");
return;
}
bool uichg = false;
@@ -73,11 +77,52 @@ void BattinfoView::update_result() {
text_current.set(to_string_dec_int(current) + " mA");
text_charge.set(current >= 0 ? "Charging" : "Discharging");
labels_opt.hidden(false);
text_ttef.hidden(false);
} else {
if (!labels_opt.hidden()) uichg = true;
labels_opt.hidden(true);
text_current.hidden(true);
text_charge.hidden(true);
text_cycles.hidden(true);
text_ttef.hidden(true);
text_warn.set("");
}
if ((valid_mask & battery::BatteryManagement::BATT_VALID_CYCLES) == battery::BatteryManagement::BATT_VALID_CYCLES) {
text_cycles.hidden(false);
uint16_t cycles = battery::BatteryManagement::get_cycles();
if (cycles < 2)
text_warn.set("SoC improves after 2 cycles");
else
text_warn.set("");
text_cycles.set(to_string_dec_uint(cycles));
} else {
text_cycles.hidden(true);
text_warn.set("");
}
if ((valid_mask & battery::BatteryManagement::BATT_VALID_TTEF) == battery::BatteryManagement::BATT_VALID_TTEF) {
text_ttef.hidden(false);
float ttef = 0;
if (current <= 0) {
ttef = battery::BatteryManagement::get_tte();
} else {
ttef = battery::BatteryManagement::get_ttf();
}
// Convert ttef to hours and minutes
uint8_t hours = static_cast<uint8_t>(ttef);
uint8_t minutes = static_cast<uint8_t>((ttef - hours) * 60 + 0.5); // +0.5 for rounding
// Create the formatted string
std::string formatted_time;
if (hours > 0) {
formatted_time += to_string_dec_uint(hours) + "h ";
}
formatted_time += to_string_dec_uint(minutes) + "m";
text_ttef.set(formatted_time);
} else {
text_ttef.hidden(true);
}
if ((valid_mask & battery::BatteryManagement::BATT_VALID_PERCENT) == battery::BatteryManagement::BATT_VALID_PERCENT) {
text_method.set("IC");
@@ -102,7 +147,10 @@ BattinfoView::BattinfoView(NavigationView& nav)
&text_charge,
&text_method,
&button_mode,
&button_exit});
&button_exit,
&text_cycles,
&text_warn,
&text_ttef});
button_exit.on_select = [this, &nav](Button&) {
nav.pop();
@@ -138,4 +186,4 @@ BattinfoView::~BattinfoView() {
thread = nullptr;
}
}
} // namespace ui
} // namespace ui
+19 -4
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
@@ -27,6 +28,7 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
#include "i2cdevmanager.hpp"
namespace ui {
class BattinfoView : public View {
@@ -55,12 +57,15 @@ class BattinfoView : public View {
{{2 * 8, 1 * 16}, "Percent:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 2 * 16}, "Voltage:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 3 * 16}, "Method:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 16}, "Change method:", Theme::getInstance()->fg_light->foreground},
};
Labels labels_opt{
{{2 * 8, 4 * 16}, "Current:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Charge:", Theme::getInstance()->fg_light->foreground}};
{{2 * 8, 5 * 16}, "Charge:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 6 * 16}, "TTF/E:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 16}, "Cycles:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 10 * 16}, "Change method:", Theme::getInstance()->fg_light->foreground},
};
Text text_percent{
{13 * 8, 1 * 16, 10 * 16, 16},
@@ -77,9 +82,19 @@ class BattinfoView : public View {
Text text_charge{
{13 * 8, 5 * 16, 10 * 16, 16},
"-"};
Text text_ttef{
{13 * 8, 6 * 16, 10 * 16, 16},
"-"};
Text text_cycles{
{13 * 8, 7 * 16, 10 * 16, 16},
"-"};
Text text_warn{
{2 * 8, 8 * 16, 30 * 8, 2 * 16},
""};
Button button_mode{
{2 * 8, 8 * 16 + 5, 5 * 16, 32},
{2 * 8, 11 * 16 + 5, 5 * 16, 32},
"Volt"};
Button button_exit{
@@ -91,4 +106,4 @@ class BattinfoView : public View {
} /* namespace ui */
#endif /*__UI_BATTINFO__*/
#endif /*__UI_BATTINFO__*/
+16 -6
View File
@@ -36,6 +36,7 @@
#include "ui_font_fixed_8x16.hpp"
#include "ui_painter.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "ui_debug_max17055.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
@@ -225,8 +226,10 @@ uint32_t RegistersWidget::reg_read(const uint32_t register_number) {
return radio::debug::second_if::register_read(register_number);
case CT_SI5351:
return portapack::clock_generator.read_register(register_number);
case CT_BATTERY:
return battery::BatteryManagement::read_register(register_number);
case CT_MAX17055: {
i2cdev::I2cDev_MAX17055* dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
return dev->read_register(register_number);
}
case CT_AUDIO:
return audio::debug::reg_read(register_number);
}
@@ -248,9 +251,11 @@ void RegistersWidget::reg_write(const uint32_t register_number, const uint32_t v
case CT_SI5351:
portapack::clock_generator.write_register(register_number, value);
break;
case CT_BATTERY:
battery::BatteryManagement::write_register(register_number, value);
case CT_MAX17055: {
i2cdev::I2cDev_MAX17055* dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
dev->write_register(register_number, value);
break;
}
case CT_AUDIO:
audio::debug::reg_write(register_number, value);
break;
@@ -464,9 +469,9 @@ void DebugPeripheralsMenuView::on_populate() {
{si5351x, Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this, si5351x]() { nav_.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 188, 96, 8}); }},
{audio::debug::codec_name(), Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_count(), audio::debug::reg_bits()}); }},
});
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BatteryModules::BATT_MAX17055) {
if (i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055)) {
add_item(
{"MAX17055", Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>("MAX17055", RegistersWidgetConfig{CT_BATTERY, 256, 16, 16}); }});
{"MAX17055", Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>("MAX17055", RegistersWidgetConfig{CT_MAX17055, 256, 16, 16}); }});
}
set_max_rows(2); // allow wider buttons
}
@@ -511,6 +516,11 @@ void DebugMenuView::on_populate() {
{"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
});
if (i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055)) {
add_item(
{"Battery", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_batt_icon, [this]() { nav_.push<BatteryCapacityView>(); }});
}
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::DEBUG, nav_)) {
add_item(gridItem);
};
+1 -1
View File
@@ -139,7 +139,7 @@ typedef enum {
CT_MAX283X,
CT_SI5351,
CT_AUDIO,
CT_BATTERY,
CT_MAX17055,
} chip_type_t;
struct RegistersWidgetConfig {
@@ -0,0 +1,111 @@
#include "ui_debug_max17055.hpp"
#include "string_format.hpp"
namespace ui {
BatteryCapacityView::RegisterEntry BatteryCapacityView::get_entry(size_t index) {
if (index < i2cdev::I2cDev_MAX17055::entries_count) {
return i2cdev::I2cDev_MAX17055::entries[index];
}
return {"", 0, "", 0, false, "", false, 0, false, false, false, 0, false};
}
BatteryCapacityView::BatteryCapacityView(NavigationView& nav) {
for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) {
name_texts[i].set_parent_rect({0 * 8, static_cast<int>((i + 1) * 16), 8 * 8, 16});
addr_texts[i].set_parent_rect({9 * 8, static_cast<int>((i + 1) * 16), 4 * 8, 16});
hex_texts[i].set_parent_rect({14 * 8, static_cast<int>((i + 1) * 16), 6 * 8, 16});
value_texts[i].set_parent_rect({21 * 8, static_cast<int>((i + 1) * 16), 10 * 8, 16});
add_child(&name_texts[i]);
add_child(&addr_texts[i]);
add_child(&hex_texts[i]);
add_child(&value_texts[i]);
}
add_children({&labels, &page_text, &button_done});
button_done.on_select = [&nav](Button&) { nav.pop(); };
auto dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
if (!dev) { // dev not found
nav.pop();
return;
}
populate_page(0);
update_page_text();
}
void BatteryCapacityView::focus() {
button_done.focus();
}
bool BatteryCapacityView::on_encoder(const EncoderEvent delta) {
int32_t new_page = current_page + delta;
if (new_page >= 0 && new_page < ((int32_t)i2cdev::I2cDev_MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE) {
current_page = new_page;
populate_page(current_page * ENTRIES_PER_PAGE);
update_page_text();
}
return true;
}
void BatteryCapacityView::update_values() {
i2cdev::I2cDev_MAX17055* dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) {
size_t entry_index = current_page * ENTRIES_PER_PAGE + i;
if (entry_index < i2cdev::I2cDev_MAX17055::entries_count) {
const auto entry = get_entry(entry_index);
uint16_t raw_value = dev->read_register(entry.address);
hex_texts[i].set("0x" + to_string_hex(raw_value, 4));
float scaled_value;
if (entry.is_signed) {
int16_t signed_value = static_cast<int16_t>(raw_value);
scaled_value = signed_value * entry.scalar;
} else {
scaled_value = raw_value * entry.scalar;
}
// Format the value with appropriate decimal places
std::string formatted_value;
if (entry.resolution > 0) {
formatted_value = to_string_decimal(scaled_value, std::min(entry.resolution, 3));
} else {
formatted_value = to_string_dec_int(scaled_value); // Show up to 3 decimal places
}
value_texts[i].set(formatted_value + " " + entry.unit);
}
}
}
void BatteryCapacityView::populate_page(int start_index) {
for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) {
size_t entry_index = start_index + i;
if (entry_index < i2cdev::I2cDev_MAX17055::entries_count) {
const auto entry = get_entry(entry_index);
name_texts[i].set(entry.name);
addr_texts[i].set("0x" + to_string_hex(entry.address, 2));
name_texts[i].hidden(false);
addr_texts[i].hidden(false);
hex_texts[i].hidden(false);
value_texts[i].hidden(false);
} else {
name_texts[i].hidden(true);
addr_texts[i].hidden(true);
hex_texts[i].hidden(true);
value_texts[i].hidden(true);
}
}
update_values();
}
void BatteryCapacityView::update_page_text() {
int total_pages = (i2cdev::I2cDev_MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
page_text.set("Page " + to_string_dec_uint(current_page + 1) + "/" + to_string_dec_uint(total_pages));
}
} // namespace ui
@@ -0,0 +1,49 @@
#ifndef __UI_DEBUG_BATTERY_HPP__
#define __UI_DEBUG_BATTERY_HPP__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "battery.hpp"
#include "i2cdevmanager.hpp"
#include "i2cdev_max17055.hpp"
namespace ui {
class BatteryCapacityView : public View {
public:
BatteryCapacityView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Battery Registers"; }
bool on_encoder(const EncoderEvent delta) override;
using RegisterEntry = i2cdev::I2cDev_MAX17055::RegisterEntry;
private:
static RegisterEntry get_entry(size_t index);
Labels labels{
{{0 * 8, 0 * 16}, "Reg", Theme::getInstance()->fg_yellow->foreground},
{{9 * 8, 0 * 16}, "Addr", Theme::getInstance()->fg_yellow->foreground},
{{14 * 8, 0 * 16}, "Hex", Theme::getInstance()->fg_yellow->foreground},
{{21 * 8, 0 * 16}, "Value", Theme::getInstance()->fg_yellow->foreground},
};
std::array<Text, 16> name_texts = {};
std::array<Text, 16> addr_texts = {};
std::array<Text, 16> hex_texts = {};
std::array<Text, 16> value_texts = {};
Text page_text{{144, 284, 80, 16}, "Page 1/1"};
Button button_done{{16, 280, 96, 24}, "Done"};
void update_values();
void populate_page(int start_index);
void update_page_text();
int current_page = 0;
static constexpr int ENTRIES_PER_PAGE = 16;
};
} // namespace ui
#endif // __UI_DEBUG_BATTERY_HPP__
+1 -9
View File
@@ -41,11 +41,6 @@ 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) {
@@ -53,7 +48,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 = (battery::BatteryManagement::isDetected() ? 11 : 10) + 2;
auto lines = 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,9 +60,6 @@ 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;
@@ -60,8 +60,6 @@ class DfuMenu : public View {
{{6 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "M4 miss:", Theme::getInstance()->fg_darkcyan->foreground},
{{6 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "Uptime:", Theme::getInstance()->fg_darkcyan->foreground}};
Labels voltage_label{{{6 * CHARACTER_WIDTH, 15 * LINE_HEIGHT}, "Voltage:", Theme::getInstance()->fg_darkcyan->foreground}};
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}, ""};
Text text_info_line_3{{15 * CHARACTER_WIDTH, 7 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
@@ -72,7 +70,6 @@ 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 {
+15 -5
View File
@@ -58,6 +58,7 @@ EncodersConfigView::EncodersConfigView(
options_enctype.on_change = [this](size_t index, int32_t) {
on_type_change(index);
set_dirty();
};
options_enctype.set_options(enc_options);
@@ -104,7 +105,18 @@ void EncodersConfigView::on_type_change(size_t index) {
// Add new SymFields.
Point pos{2 * 8, 9 * 8};
std::string format_string;
std::string format_string = encoder_def->word_format;
/*important notes of this format_String
* previously before PR#1444, this code using the for loop below to generate the format_string, which MAYBE to prevent showing the S symble.
* but after PR#1444, the entireh of symfield code not compatible with this design anymore.
* however the encoder_def struct itself has a field called word_format, which exactly be avle to use as the format_string,
* because itself is ONLY a hint text, not impact protocol things.
* When you find OOK protocol is broken and see this commit from git blame, please be aware that it's not from here.
* previously me and @cusspvz already found it broken, this commit is just to fix the broken that from PR#1444.
* @zxkmm
*/
uint8_t word_length = encoder_def->word_length;
auto on_change_handler = [this](SymField&) {
generate_frame();
@@ -119,11 +131,9 @@ void EncodersConfigView::on_type_change(size_t index) {
switch (symbol_type) {
case 'A':
symfield->set_symbol_list(encoder_def->address_symbols);
format_string += 'A';
break;
case 'D':
symfield->set_symbol_list(encoder_def->data_symbols);
format_string += 'D';
break;
}
@@ -131,8 +141,8 @@ void EncodersConfigView::on_type_change(size_t index) {
pos += Point{8, 0};
}
// Ugly :( Pad to erase
format_string.append(24 - format_string.size(), ' ');
// cut the S, cuz sync bit isn't in symfield for user to chage/edit.
format_string.erase(std::remove(format_string.begin(), format_string.end(), 'S'), format_string.end());
text_format.set(format_string);
generate_frame();
@@ -98,7 +98,6 @@ rf::Frequency GlassView::get_freq_from_bin_pos(uint8_t pos) {
freq_at_pos = f_center_ini + ((pos - 120) * ((looking_glass_range - ((16 * looking_glass_range) / SPEC_NB_BINS)) / 2)) / (SCREEN_W / 2);
} else
freq_at_pos = f_min + (2 * offset * each_bin_size) + (pos * looking_glass_range) / SCREEN_W;
return freq_at_pos;
}
@@ -257,7 +256,7 @@ void GlassView::on_range_changed() {
bin_length = SCREEN_W;
ignore_dc = 0;
looking_glass_bandwidth = looking_glass_range;
looking_glass_sampling_rate = looking_glass_bandwidth;
looking_glass_sampling_rate = looking_glass_range;
each_bin_size = looking_glass_bandwidth / SCREEN_W;
looking_glass_step = looking_glass_bandwidth;
f_center_ini = f_min + (looking_glass_bandwidth / 2); // Initial center frequency for sweep
@@ -265,7 +264,7 @@ void GlassView::on_range_changed() {
// view is made in multiple pass, use original bin picking
mode = scan_type.selected_index_value();
looking_glass_bandwidth = LOOKING_GLASS_SLICE_WIDTH_MAX;
looking_glass_sampling_rate = LOOKING_GLASS_SLICE_WIDTH_MAX;
looking_glass_sampling_rate = LOOKING_GLASS_MAX_SAMPLERATE;
each_bin_size = LOOKING_GLASS_SLICE_WIDTH_MAX / SPEC_NB_BINS;
if (mode == LOOKING_GLASS_FASTSCAN) {
offset = 2;
@@ -315,8 +314,8 @@ void GlassView::update_min(int32_t v) {
int32_t min_size = steps;
if (locked_range)
min_size = search_span;
if (min_size < 2)
min_size = 2;
if (min_size < 1)
min_size = 1;
if (v > 7200 - min_size) {
v = 7200 - min_size;
}
@@ -332,8 +331,8 @@ void GlassView::update_max(int32_t v) {
int32_t min_size = steps;
if (locked_range)
min_size = search_span;
if (min_size < 2)
min_size = 2;
if (min_size < 1)
min_size = 1;
if (v < min_size) {
v = min_size;
}
@@ -487,7 +486,12 @@ GlassView::GlassView(
range_presets.set_selected_index(preset_index);
field_marker.on_encoder_change = [this](TextField&, EncoderEvent delta) {
marker_pixel_index = clip<uint8_t>(marker_pixel_index + delta, 0, SCREEN_W);
if ((marker_pixel_index + delta) < 0)
marker_pixel_index = marker_pixel_index + delta + SCREEN_W;
else if ((marker_pixel_index + delta) > SCREEN_W)
marker_pixel_index = marker_pixel_index + delta - SCREEN_W;
else
marker_pixel_index = marker_pixel_index + delta;
on_marker_change();
};
@@ -561,6 +565,17 @@ GlassView::GlassView(
update_display_beep();
}
void GlassView::on_freqchg(int64_t freq) {
int64_t half_range = abs(field_frequency_max.value() - field_frequency_max.value()) / 2;
if (half_range < 1) {
half_range = 1;
}
range_presets.set_selected_index(0); // Manual
update_min(freq - half_range);
update_max(freq + half_range);
on_range_changed();
}
uint8_t GlassView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value
return iq_phase_calibration_value;
}
@@ -40,6 +40,7 @@
namespace ui {
#define LOOKING_GLASS_SLICE_WIDTH_MAX 20000000
#define LOOKING_GLASS_MAX_SAMPLERATE 20000000
#define MHZ_DIV 1000000
// blanked DC (16 centered bins ignored ) and top left and right (2 bins ignored on each side )
@@ -109,6 +110,7 @@ class GlassView : public View {
std::string label{};
};
void on_freqchg(int64_t freq);
int32_t map(int32_t value, int32_t fromLow, int32_t fromHigh, int32_t toLow, int32_t toHigh);
std::vector<preset_entry> presets_db{};
void manage_beep_audio();
@@ -307,6 +309,7 @@ class GlassView : public View {
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
this->fifo = message.fifo;
}};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
@@ -317,6 +320,13 @@ class GlassView : public View {
}
}
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
};
} // namespace ui
#endif
+28 -1
View File
@@ -5,6 +5,7 @@
* Copyright (C) 2023 Kyle Reed
* Copyright (C) 2024 Mark Thompson
* Copyright (C) 2024 u-foka
* Copyright (C) 2024 HTotoo
* Copyleft (ɔ) 2024 zxkmm under GPL license
*
* This file is part of PortaPack.
@@ -49,6 +50,8 @@ namespace fs = std::filesystem;
#include "ui_font_fixed_8x16.hpp"
#include "cpld_update.hpp"
#include "config_mode.hpp"
#include "i2cdevmanager.hpp"
#include "i2cdev_max17055.hpp"
extern ui::SystemView* system_view_ptr;
@@ -798,6 +801,13 @@ SetDisplayView::SetDisplayView(NavigationView& nav) {
send_system_refresh();
nav.pop();
};
// only enable invert OR fake brightness
checkbox_invert_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_brightness_switch.set_value(false);
};
checkbox_brightness_switch.on_select = [this](Checkbox&, bool v) {
if (v) checkbox_invert_switch.set_value(false);
};
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
@@ -869,6 +879,7 @@ SetAutostartView::SetAutostartView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&button_reset,
&options});
button_save.on_select = [&nav, this](Button&) {
@@ -885,6 +896,12 @@ SetAutostartView::SetAutostartView(NavigationView& nav) {
nav.pop();
};
button_reset.on_select = [this](Button&) {
selected = 0;
options.set_selected_index(0);
autostart_app = "";
};
// options
i = 0;
OptionsField::option_t o{"-none-", i};
@@ -962,6 +979,8 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
&button_cancel,
&checkbox_overridebatt});
if (i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055)) add_children({&button_reset, &labels2});
button_save.on_select = [&nav, this](Button&) {
pmem::set_ui_override_batt_calc(checkbox_overridebatt.value());
battery::BatteryManagement::set_calc_override(checkbox_overridebatt.value());
@@ -969,6 +988,14 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
nav.pop();
};
button_reset.on_select = [&nav, this](Button&) {
auto dev = (i2cdev::I2cDev_MAX17055*)i2cdev::I2CDevManager::get_dev_by_model(I2C_DEVMDL::I2CDEVMDL_MAX17055);
if (dev->reset_learned())
nav.display_modal("Reset", "Battery parameters reset");
else
nav.display_modal("Error", "Error parameter reset");
};
checkbox_overridebatt.set_value(pmem::ui_override_batt_calc());
button_cancel.on_select = [&nav, this](Button&) {
@@ -1010,7 +1037,7 @@ void SettingsMenuView::on_populate() {
{"Theme", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetThemeView>(); }},
{"Autostart", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetAutostartView>(); }},
});
if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetBatteryView>(); }});
if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_batt_icon, [this]() { nav_.push<SetBatteryView>(); }});
}
} /* namespace ui */
+20 -8
View File
@@ -843,14 +843,19 @@ class SetAutostartView : public View {
"Save"};
OptionsField options{
{8 * 8, 4 * 16},
30,
{}};
{0 * 8, 4 * 16},
screen_width / 8,
{},
true};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
Button button_reset{
{2 * 8, 6 * 16, screen_width - 4 * 8, 32},
"Reset"};
};
class SetThemeView : public View {
@@ -872,15 +877,16 @@ class SetThemeView : public View {
"Save"};
OptionsField options{
{8 * 8, 4 * 16},
30,
{0 * 8, 4 * 16},
screen_width / 8,
{
{"Default - Grey", 0},
{"Yellow", 1},
{"Aqua", 2},
{"Green", 3},
{"Red", 4},
}};
},
true};
Checkbox checkbox_menuset{
{2 * 8, 6 * 16},
@@ -906,13 +912,14 @@ class SetBatteryView : public View {
Labels labels{
{{1 * 8, 1 * 16}, "Override batt calculation", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "method to voltage based", Theme::getInstance()->fg_light->foreground}};
Labels labels2{
{{1 * 8, 6 * 16}, "Reset IC's learned params.", Theme::getInstance()->fg_light->foreground}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
Checkbox checkbox_overridebatt{
{2 * 8, 6 * 16},
{2 * 8, 4 * 16},
23,
"Override"};
@@ -920,6 +927,11 @@ class SetBatteryView : public View {
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
Button button_reset{
{2 * 8, 8 * 16, 12 * 8, 32},
"Reset",
};
};
class SettingsMenuView : public BtnGridView {
+319 -313
View File
@@ -1,321 +1,327 @@
// converted by bmp_to_hex_cpp_arr.py at the firmware/tools dir
// fake transparent px should be the exact color rgb dec(41,24,22), then use true for the last arg when drawing bmp with the draw func
const unsigned char splash_bmp[] = {
0x42, 0x4d, 0xf4, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00,
0x42, 0x4d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xe6, 0x00, 0x00, 0x00, 0x32, 0x00,
0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x82, 0x0e,
0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x9c, 0x0e,
0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x12, 0x0b, 0x00, 0x00, 0x0f, 0x00,
0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x19, 0xf6, 0x00, 0x00, 0x14,
0xc9, 0x00, 0x00, 0x0c, 0x78, 0x00, 0x16, 0x18, 0x29, 0x00, 0xfd, 0xfd,
0xfd, 0x00, 0xf0, 0xf0, 0xf0, 0x00, 0xe7, 0xe7, 0xe7, 0x00, 0xda, 0xda,
0xda, 0x00, 0xcd, 0xcd, 0xcd, 0x00, 0xc7, 0xc7, 0xc7, 0x00, 0xba, 0xba,
0xba, 0x00, 0xad, 0xad, 0xad, 0x00, 0x96, 0x96, 0x96, 0x00, 0x5c, 0x5c,
0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee,
0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0x00, 0x18,
0xee, 0x44, 0x44, 0x47, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0xd5, 0x44, 0x7d,
0x08, 0xee, 0x00, 0x14, 0xe3, 0x44, 0x44, 0x48, 0x3e, 0xea, 0x44, 0x44,
0x44, 0xde, 0x12, 0xee, 0x00, 0x08, 0xa4, 0x44, 0x45, 0x9d, 0x08, 0xee,
0x00, 0x08, 0xc4, 0x44, 0x45, 0xce, 0x10, 0xee, 0x00, 0x08, 0x34, 0x44,
0x44, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x44, 0x44, 0x6d, 0xee, 0xee,
0xee, 0xe3, 0xd5, 0x00, 0x1a, 0x44, 0x00, 0x1c, 0x45, 0xce, 0xee, 0x44,
0x44, 0x47, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0xd5, 0x44, 0x7d, 0x08, 0xee,
0x00, 0x0a, 0xe3, 0x44, 0x44, 0x48, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x34,
0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xee, 0xd4, 0x46, 0x89, 0x98,
0xb3, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xed, 0x79, 0x99,
0x99, 0x73, 0x10, 0xee, 0x00, 0x0a, 0xed, 0x47, 0x99, 0x95, 0xae, 0x00,
0x08, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee, 0x00, 0x08,
0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x14, 0x34, 0x99, 0x99, 0x7c,
0xee, 0xee, 0xee, 0xb4, 0x56, 0x89, 0x18, 0x99, 0x00, 0x2e, 0x97, 0xae,
0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xee, 0xd4, 0x46, 0x89, 0x98,
0xb3, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x34, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xe3, 0x55, 0x99,
0x99, 0x99, 0x9b, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee,
0xb9, 0x99, 0x99, 0x8d, 0x10, 0xee, 0x00, 0x0a, 0xe7, 0x69, 0x99, 0x74,
0xde, 0x00, 0x08, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee,
0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99,
0x99, 0x7c, 0xee, 0xee, 0xea, 0x47, 0x1c, 0x99, 0x00, 0x2e, 0x97, 0xae,
0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xe3, 0x55, 0x99, 0x99, 0x99,
0x9b, 0xee, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x36, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xea, 0x48, 0x99,
0x99, 0x99, 0x98, 0xde, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee,
0xd7, 0x99, 0x99, 0x98, 0x3e, 0x00, 0x0e, 0xee, 0x00, 0x08, 0xd4, 0x89,
0x99, 0x5a, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae, 0x10, 0xee,
0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99,
0x99, 0x7c, 0xee, 0xee, 0xb4, 0x79, 0x1c, 0x99, 0x00, 0x2e, 0x97, 0xae,
0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xea, 0x48, 0x99, 0x99, 0x99,
0x98, 0xde, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x12, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xd4, 0x79, 0x00,
0x08, 0x99, 0x00, 0x1c, 0x73, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e,
0xee, 0xeb, 0x99, 0x99, 0x99, 0xce, 0x0c, 0xee, 0x00, 0x0a, 0xe3, 0x56,
0x99, 0x97, 0x4d, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae,
0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x0e,
0x34, 0x99, 0x99, 0x7c, 0xee, 0xed, 0x47, 0x00, 0x1e, 0x99, 0x00, 0x16,
0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xd4, 0x79, 0x00,
0x08, 0x99, 0x00, 0x10, 0x73, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e,
0x00, 0x00, 0x00, 0x10, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xa5,
0x0a, 0x99, 0x00, 0x1c, 0x8d, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e,
0xee, 0xed, 0x79, 0x99, 0x99, 0x73, 0x0c, 0xee, 0x00, 0x0a, 0xec, 0x48,
0x99, 0x95, 0xae, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xae,
0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x0e,
0x34, 0x99, 0x99, 0x7c, 0xee, 0xe7, 0x69, 0x00, 0x1e, 0x99, 0x00, 0x14,
0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xee, 0xa5, 0x0a, 0x99,
0x00, 0x10, 0x8d, 0xee, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00,
0x00, 0x36, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xed, 0x47, 0x99, 0x97,
0x89, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee,
0xee, 0xb9, 0x99, 0x99, 0x9d, 0x00, 0x0c, 0xee, 0x00, 0x0a, 0x34, 0x69,
0x99, 0x74, 0xde, 0x00, 0x0a, 0xee, 0x00, 0x08, 0xc7, 0x99, 0x98, 0xae,
0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0xee, 0x34, 0x89, 0x99, 0x64, 0x00, 0x1c, 0x44,
0x00, 0x2c, 0xae, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xed, 0x47, 0x99,
0x97, 0x89, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e,
0x00, 0x00, 0x00, 0x38, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0xec, 0x59,
0x99, 0x95, 0xbc, 0x99, 0x99, 0x98, 0xde, 0xee, 0xe3, 0x49, 0x99, 0x94,
0x3e, 0xee, 0xee, 0xd7, 0x99, 0x99, 0x99, 0x74, 0x0c, 0x44, 0x00, 0x06,
0x99, 0x99, 0x4b, 0x00, 0x0c, 0xee, 0x00, 0x08, 0xb7, 0x99, 0x99, 0xae,
0x10, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0xee, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0xee, 0xd5, 0x99, 0x99, 0x6d, 0x00, 0x20, 0xee,
0x00, 0x28, 0x49, 0x99, 0x95, 0xde, 0xee, 0xec, 0x59, 0x99, 0x95, 0xbc,
0x99, 0x99, 0x98, 0xde, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00,
0x00, 0x30, 0xee, 0x49, 0x99, 0x95, 0xde, 0xee, 0x34, 0x79, 0x99, 0x84,
0xdd, 0x89, 0x99, 0x99, 0x8e, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee,
0xee, 0xeb, 0x16, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0xee, 0x00, 0x0a,
0xed, 0x47, 0x99, 0x99, 0x9d, 0x00, 0x10, 0xee, 0x00, 0x08, 0x34, 0x99,
0x99, 0x9d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6,
0x99, 0x99, 0x6d, 0x00, 0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x95, 0xde,
0xee, 0x34, 0x79, 0x99, 0x84, 0xdd, 0x89, 0x99, 0x99, 0x8e, 0xee, 0xe3,
0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x32, 0xee, 0x49, 0x99, 0x95,
0xde, 0xee, 0xc4, 0x99, 0x99, 0x67, 0xee, 0x79, 0x99, 0x99, 0x8d, 0xee,
0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xed, 0x79, 0x00, 0x14, 0x99,
0x02, 0x94, 0x02, 0xce, 0x0a, 0xee, 0x00, 0x0c, 0x35, 0x59, 0x99, 0x99,
0x98, 0x3e, 0x0e, 0xee, 0x00, 0x0a, 0x34, 0x99, 0x99, 0x99, 0x74, 0x00,
0x16, 0x44, 0x00, 0x10, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x9d,
0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x95, 0xde, 0xee, 0xc4, 0x99, 0x99,
0x67, 0xee, 0x79, 0x99, 0x99, 0x8d, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e,
0x00, 0x00, 0x00, 0x32, 0xee, 0x49, 0x99, 0x95, 0xde, 0xe3, 0x57, 0x99,
0x99, 0x4d, 0xee, 0xd8, 0x99, 0x99, 0x98, 0xee, 0xe3, 0x49, 0x99, 0x94,
0x3e, 0xee, 0xee, 0xee, 0xc9, 0x00, 0x14, 0x99, 0x02, 0x74, 0x02, 0x3e,
0x08, 0xee, 0x00, 0x0e, 0xe3, 0x84, 0x89, 0x99, 0x99, 0x99, 0xa3, 0x00,
0x0e, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x0e, 0x7c, 0xee, 0xd6, 0x99,
0x99, 0x99, 0x64, 0x00, 0x18, 0x44, 0x00, 0x2e, 0x45, 0xce, 0xee, 0x49,
0x99, 0x95, 0xde, 0xe3, 0x57, 0x99, 0x99, 0x4d, 0xee, 0xd8, 0x99, 0x99,
0x98, 0xee, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x32,
0xee, 0x49, 0x99, 0x95, 0xde, 0xed, 0x48, 0x99, 0x97, 0x43, 0xee, 0x37,
0x99, 0x99, 0x97, 0xde, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xee,
0x37, 0x00, 0x14, 0x99, 0x02, 0x4c, 0x0a, 0xee, 0x02, 0xeb, 0x02, 0x48,
0x08, 0x99, 0x02, 0x9b, 0x0e, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06,
0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49,
0x99, 0x95, 0xde, 0xed, 0x48, 0x99, 0x97, 0x43, 0xee, 0x37, 0x99, 0x99,
0x97, 0xde, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x32,
0xee, 0x49, 0x99, 0x95, 0xde, 0x35, 0x69, 0x99, 0x95, 0xce, 0xee, 0xec,
0x89, 0x99, 0x99, 0xbe, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee, 0xee, 0xee,
0xec, 0x00, 0x12, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0xee, 0x02, 0xd4,
0x02, 0x79, 0x0a, 0x99, 0x02, 0xde, 0x0c, 0xee, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae,
0xee, 0x49, 0x99, 0x95, 0xde, 0x35, 0x69, 0x99, 0x95, 0xce, 0xee, 0xec,
0x89, 0x99, 0x99, 0xbe, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x48, 0xee, 0x49, 0x99, 0x95, 0xde, 0xd4, 0x89, 0x99, 0x74, 0x3e,
0xee, 0xe3, 0x79, 0x99, 0x99, 0x7d, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0xee,
0xee, 0xee, 0xe3, 0x79, 0x99, 0x99, 0x64, 0x44, 0x44, 0x89, 0x99, 0x99,
0x94, 0xce, 0x08, 0xee, 0x00, 0x12, 0xed, 0x46, 0x99, 0x98, 0x59, 0x99,
0x99, 0x99, 0x8d, 0x00, 0x0c, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06,
0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49,
0x99, 0x95, 0xde, 0xd4, 0x89, 0x99, 0x74, 0x3e, 0xee, 0xe3, 0x79, 0x99,
0x99, 0x7d, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a,
0xee, 0x49, 0x99, 0x95, 0xde, 0x76, 0x99, 0x99, 0x6a, 0xee, 0xee, 0xee,
0xa9, 0x99, 0x99, 0x9b, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x08, 0xee,
0x00, 0x16, 0xc9, 0x99, 0x99, 0x9c, 0xee, 0xee, 0x3b, 0x99, 0x99, 0x74,
0x3e, 0x00, 0x08, 0xee, 0x00, 0x14, 0x35, 0x59, 0x99, 0x95, 0x7d, 0x99,
0x99, 0x99, 0x9a, 0x3e, 0x0a, 0xee, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06,
0x7c, 0xee, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xae, 0xee, 0x49,
0x99, 0x95, 0xde, 0x76, 0x99, 0x99, 0x6a, 0xee, 0xee, 0xee, 0xa9, 0x99,
0x99, 0x9b, 0xe3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a,
0xee, 0x49, 0x99, 0x95, 0x3d, 0x48, 0x99, 0x98, 0x4d, 0xee, 0xee, 0xee,
0xd7, 0x99, 0x99, 0x97, 0xd3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x08, 0xee,
0x00, 0x14, 0x37, 0x99, 0x99, 0x97, 0x3e, 0xee, 0xc4, 0x89, 0x99, 0x4c,
0x08, 0xee, 0x00, 0x16, 0xe3, 0x84, 0x89, 0x99, 0x64, 0x3e, 0xd9, 0x99,
0x99, 0x99, 0xa3, 0x00, 0x0a, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x64,
0x16, 0x44, 0x00, 0x0c, 0x99, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x20, 0x99,
0x00, 0x2e, 0x97, 0xae, 0xee, 0x49, 0x99, 0x95, 0x3d, 0x48, 0x99, 0x98,
0x4d, 0xee, 0xee, 0xee, 0xd7, 0x99, 0x99, 0x97, 0xd3, 0x49, 0x99, 0x94,
0x3e, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xee, 0x49, 0x99, 0x96, 0x3b, 0x69,
0x99, 0x96, 0x5e, 0xee, 0xee, 0xee, 0xe8, 0x99, 0x99, 0x99, 0xc3, 0x49,
0x99, 0x94, 0x3e, 0x00, 0x08, 0xee, 0x00, 0x14, 0xec, 0x99, 0x99, 0x99,
0xbe, 0xe3, 0x46, 0x99, 0x96, 0x43, 0x08, 0xee, 0x00, 0x16, 0xeb, 0x48,
0x99, 0x97, 0x4d, 0xee, 0xed, 0x99, 0x99, 0x99, 0x9c, 0x00, 0x0a, 0xee,
0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x39, 0x99,
0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x54, 0x00, 0x1c, 0x44, 0x00, 0x2c,
0xae, 0xee, 0x49, 0x99, 0x96, 0x3b, 0x69, 0x99, 0x96, 0x5e, 0xee, 0xee,
0xee, 0xe8, 0x99, 0x99, 0x99, 0xc3, 0x49, 0x99, 0x94, 0x3e, 0x00, 0x00,
0x00, 0x2a, 0xee, 0x49, 0x99, 0x98, 0xd5, 0x79, 0x99, 0x94, 0xde, 0xee,
0xee, 0xee, 0xed, 0x89, 0x99, 0x99, 0x73, 0x49, 0x99, 0x94, 0x3e, 0x00,
0x08, 0xee, 0x00, 0x14, 0xe3, 0x79, 0x99, 0x99, 0x8d, 0xea, 0x59, 0x99,
0x84, 0xde, 0x08, 0xee, 0x00, 0x18, 0xd4, 0x79, 0x99, 0x84, 0xbe, 0xee,
0xee, 0xb9, 0x99, 0x99, 0x99, 0xde, 0x08, 0xee, 0x00, 0x08, 0x34, 0x99,
0x99, 0x6d, 0x16, 0xee, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6,
0x99, 0x99, 0x5d, 0x00, 0x20, 0xee, 0x00, 0x28, 0x49, 0x99, 0x98, 0xd5,
0x79, 0x99, 0x94, 0xde, 0xee, 0xee, 0xee, 0xed, 0x89, 0x99, 0x99, 0x73,
0x49, 0x99, 0x94, 0x3e, 0x00, 0x00, 0x00, 0x2a, 0xee, 0x49, 0x99, 0x99,
0xa6, 0x99, 0x99, 0x74, 0x3e, 0xee, 0xee, 0xee, 0xe3, 0x79, 0x99, 0x99,
0x9c, 0x59, 0x99, 0x94, 0x3e, 0x00, 0x0a, 0xee, 0x00, 0x32, 0xc9, 0x99,
0x99, 0x9a, 0xd4, 0x79, 0x99, 0x65, 0x3e, 0xee, 0xee, 0xee, 0xed, 0x46,
0x99, 0x98, 0x48, 0x3e, 0xee, 0xee, 0x3a, 0x99, 0x99, 0x99, 0x83, 0x00,
0x08, 0xee, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0xee, 0xd6, 0x99, 0x99, 0x93, 0x00, 0x20, 0xee,
0x00, 0x28, 0x49, 0x99, 0x99, 0xa6, 0x99, 0x99, 0x74, 0x3e, 0xee, 0xee,
0xee, 0xe3, 0x79, 0x99, 0x99, 0x9c, 0x59, 0x99, 0x94, 0x3e, 0x00, 0x00,
0x00, 0x10, 0xee, 0x49, 0x99, 0x99, 0x98, 0x99, 0x99, 0x5c, 0x0a, 0xee,
0x00, 0x10, 0xd8, 0x99, 0x99, 0x99, 0x79, 0x99, 0x84, 0x3e, 0x0a, 0xee,
0x00, 0x10, 0x37, 0x99, 0x99, 0x99, 0x86, 0x99, 0x98, 0x4d, 0x08, 0xee,
0x00, 0x2a, 0x35, 0x59, 0x99, 0x95, 0x53, 0xee, 0xee, 0xee, 0xe3, 0x89,
0x99, 0x99, 0x9a, 0x3e, 0xee, 0xee, 0xee, 0x34, 0x99, 0x99, 0x6d, 0x00,
0x16, 0xee, 0x00, 0x14, 0x34, 0x99, 0x99, 0x7c, 0xee, 0x36, 0x99, 0x99,
0x99, 0x54, 0x18, 0x44, 0x00, 0x14, 0x45, 0xce, 0xee, 0x49, 0x99, 0x99,
0x98, 0x99, 0x99, 0x5c, 0x0a, 0xee, 0x00, 0x10, 0xd8, 0x99, 0x99, 0x99,
0x79, 0x99, 0x84, 0x3e, 0x00, 0x00, 0x02, 0xee, 0x02, 0x59, 0x08, 0x99,
0x02, 0x97, 0x02, 0x4d, 0x0a, 0xee, 0x02, 0x36, 0x0a, 0x99, 0x02, 0x75,
0x0c, 0xee, 0x02, 0xec, 0x0a, 0x99, 0x00, 0x16, 0x96, 0x53, 0xee, 0xee,
0xee, 0xe3, 0x84, 0x89, 0x99, 0x64, 0xde, 0x00, 0x08, 0xee, 0x00, 0x18,
0xd9, 0x99, 0x99, 0x99, 0xbe, 0xee, 0xee, 0xee, 0x34, 0x99, 0x99, 0x6d,
0x16, 0xee, 0x00, 0x0c, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xe6, 0x20, 0x99,
0x00, 0x08, 0x97, 0xae, 0xee, 0x59, 0x08, 0x99, 0x02, 0x97, 0x02, 0x4d,
0x0a, 0xee, 0x02, 0x36, 0x0a, 0x99, 0x02, 0x75, 0x02, 0xee, 0x00, 0x00,
0x02, 0xee, 0x02, 0xa8, 0x08, 0x99, 0x02, 0x95, 0x02, 0xae, 0x0a, 0xee,
0x02, 0xec, 0x0a, 0x99, 0x02, 0x5b, 0x0c, 0xee, 0x02, 0xe3, 0x02, 0x89,
0x08, 0x99, 0x00, 0x14, 0x84, 0xde, 0xee, 0xee, 0xee, 0xeb, 0x48, 0x99,
0x97, 0x4c, 0x0a, 0xee, 0x00, 0x18, 0xed, 0x99, 0x99, 0x99, 0x9d, 0xee,
0xee, 0xee, 0x34, 0x99, 0x99, 0x6d, 0x16, 0xee, 0x00, 0x0e, 0x34, 0x99,
0x99, 0x7c, 0xee, 0xed, 0x89, 0x00, 0x1e, 0x99, 0x00, 0x08, 0x97, 0xae,
0xee, 0xa8, 0x08, 0x99, 0x02, 0x95, 0x02, 0xae, 0x0a, 0xee, 0x02, 0xec,
0x0a, 0x99, 0x02, 0x5b, 0x02, 0xee, 0x00, 0x00, 0x02, 0xee, 0x02, 0xd7,
0x08, 0x99, 0x02, 0x64, 0x02, 0x3e, 0x0a, 0xee, 0x00, 0x0e, 0xe3, 0x79,
0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0c, 0xd8, 0x99,
0x99, 0x99, 0x98, 0x48, 0x08, 0xee, 0x00, 0x0a, 0xd4, 0x79, 0x99, 0x84,
0xbe, 0x00, 0x0c, 0xee, 0x00, 0x16, 0xb9, 0x99, 0x99, 0x98, 0xde, 0xee,
0xee, 0x34, 0x99, 0x99, 0x6d, 0x00, 0x16, 0xee, 0x00, 0x0c, 0x34, 0x99,
0x99, 0x7c, 0xee, 0xe3, 0x20, 0x99, 0x00, 0x08, 0x97, 0xae, 0xee, 0xd7,
0x08, 0x99, 0x02, 0x64, 0x02, 0x3e, 0x0a, 0xee, 0x00, 0x10, 0xe3, 0x79,
0x99, 0x99, 0x99, 0x96, 0x4d, 0xee, 0x00, 0x00, 0x00, 0x0e, 0xee, 0xec,
0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0c, 0xd9, 0x99,
0x99, 0x99, 0x74, 0xce, 0x0e, 0xee, 0x00, 0x1e, 0xed, 0x99, 0x99, 0x99,
0x85, 0x53, 0xee, 0xee, 0xee, 0xed, 0x46, 0x99, 0x99, 0x57, 0x3e, 0x00,
0x0c, 0xee, 0x00, 0x16, 0x39, 0x99, 0x99, 0x99, 0xa3, 0xee, 0xee, 0x34,
0x99, 0x99, 0x6d, 0x00, 0x16, 0xee, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c,
0xee, 0xee, 0x3a, 0x00, 0x1e, 0x99, 0x00, 0x12, 0x97, 0xae, 0xee, 0xec,
0x99, 0x99, 0x99, 0x96, 0x4d, 0x00, 0x0e, 0xee, 0x00, 0x0e, 0xd9, 0x99,
0x99, 0x99, 0x74, 0xce, 0xee, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xee, 0xe3,
0xc8, 0x99, 0x96, 0x44, 0xde, 0x00, 0x0e, 0xee, 0x00, 0x0a, 0xed, 0x79,
0x99, 0x76, 0x4b, 0x00, 0x12, 0xee, 0x00, 0x1a, 0xc7, 0x99, 0x96, 0x45,
0xde, 0xee, 0xee, 0xee, 0x35, 0x59, 0x99, 0x96, 0x43, 0x00, 0x0e, 0xee,
0x00, 0x16, 0xe3, 0x89, 0x99, 0x99, 0x9a, 0x3e, 0xee, 0x34, 0x99, 0x99,
0x6d, 0x00, 0x16, 0xee, 0x00, 0x10, 0x34, 0x99, 0x99, 0x7c, 0xee, 0xee,
0xe3, 0xa6, 0x1c, 0x99, 0x00, 0x12, 0x97, 0xae, 0xee, 0xe3, 0xc8, 0x99,
0x96, 0x44, 0xde, 0x00, 0x0e, 0xee, 0x00, 0x0e, 0xed, 0x79, 0x99, 0x76,
0x4b, 0xee, 0xee, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xee, 0xee, 0xed, 0x84,
0x44, 0xc3, 0x12, 0xee, 0x00, 0x08, 0xdc, 0x44, 0x4a, 0xde, 0x12, 0xee,
0x00, 0x1a, 0xed, 0xa4, 0x44, 0xd3, 0xee, 0xee, 0xee, 0xe3, 0xc8, 0x44,
0x44, 0x44, 0xde, 0x00, 0x10, 0xee, 0x00, 0x14, 0xd4, 0x44, 0x44, 0x44,
0xae, 0xee, 0x38, 0x44, 0x44, 0x4d, 0x16, 0xee, 0x00, 0x12, 0x38, 0x44,
0x44, 0x4c, 0xee, 0xee, 0xee, 0xed, 0xc4, 0x00, 0x1c, 0x44, 0x00, 0x0e,
0xae, 0xee, 0xee, 0xed, 0x84, 0x44, 0xc3, 0x00, 0x12, 0xee, 0x00, 0x0c,
0xdc, 0x44, 0x4a, 0xde, 0xee, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00,
0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00,
0xe6, 0xee, 0x00, 0x00, 0x00, 0x1e, 0xee, 0x20, 0x00, 0x2e, 0xe0, 0x00,
0x13, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0xe2, 0x00, 0x08, 0x00,
0x00, 0x26, 0x1e, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0x00, 0x00,
0x13, 0xe3, 0x00, 0x00, 0x1e, 0xe2, 0x00, 0x00, 0x2e, 0x00, 0x24, 0xee,
0x00, 0x06, 0xe2, 0x00, 0x02, 0x00, 0x08, 0xee, 0x00, 0x50, 0xe2, 0x00,
0x00, 0x00, 0x01, 0xee, 0xe1, 0x00, 0x02, 0xe3, 0x00, 0x01, 0xee, 0xee,
0xe2, 0x00, 0x02, 0xee, 0xee, 0xe0, 0x00, 0x13, 0xe2, 0x00, 0x02, 0xee,
0x00, 0x01, 0x3e, 0xee, 0xee, 0xee, 0x30, 0x00, 0x1e, 0xe1, 0x00, 0x02,
0xee, 0x30, 0x08, 0x00, 0x00, 0x10, 0x2e, 0x30, 0x00, 0x1e, 0xe1, 0x00,
0x02, 0xee, 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11,
0x03, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x10, 0x10, 0x00, 0x00,
0x00, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x03,
0xe3, 0x01, 0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x06,
0xe2, 0x01, 0x11, 0x00, 0x08, 0xee, 0x00, 0x68, 0x10, 0x10, 0x00, 0x11,
0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01,
0x11, 0xee, 0xee, 0xe0, 0x11, 0x03, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x10,
0x3e, 0xee, 0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x00,
0x10, 0x00, 0x00, 0x00, 0x2e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee,
0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1,
0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee,
0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x13, 0xe3, 0x01,
0x10, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x06, 0xe2, 0x01,
0x11, 0x00, 0x08, 0xee, 0x00, 0x52, 0x11, 0x10, 0x2e, 0x31, 0x11, 0x0e,
0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x11, 0xee,
0xee, 0xe0, 0x11, 0x03, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x10, 0x3e, 0xee,
0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0x00,
0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00,
0x00, 0x00, 0x00, 0x50, 0xee, 0x20, 0x11, 0x2e, 0xe0, 0x11, 0x03, 0xe1,
0x11, 0x12, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee,
0xe1, 0x11, 0x12, 0xe3, 0x01, 0x00, 0xee, 0x01, 0x11, 0x11, 0x00, 0x01,
0x12, 0xee, 0xe2, 0x01, 0x11, 0x11, 0x00, 0x2e, 0x20, 0xee, 0x00, 0x06,
0xe2, 0x01, 0x12, 0x00, 0x08, 0xee, 0x00, 0x52, 0x11, 0x10, 0x2e, 0x30,
0x11, 0x0e, 0xe1, 0x11, 0x12, 0xe3, 0x01, 0x00, 0xee, 0xee, 0xe2, 0x01,
0x11, 0xee, 0xee, 0xe0, 0x11, 0x13, 0xe2, 0x01, 0x11, 0xee, 0x01, 0x11,
0x3e, 0xee, 0xee, 0xee, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01,
0x10, 0x00, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02,
0xee, 0x00, 0x00, 0x00, 0x00, 0x38, 0xee, 0x20, 0x11, 0x11, 0x00, 0x11,
0x03, 0xe1, 0x11, 0x11, 0x00, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee,
0xee, 0xee, 0xe1, 0x11, 0x11, 0x00, 0x01, 0x1e, 0xee, 0x01, 0x08, 0x11,
0x00, 0x10, 0x12, 0xee, 0xe2, 0x01, 0x11, 0x11, 0x10, 0x2e, 0x20, 0xee,
0x00, 0x60, 0xe2, 0x01, 0x11, 0x10, 0x00, 0x03, 0xee, 0x11, 0x10, 0x2e,
0x30, 0x11, 0x0e, 0xe1, 0x11, 0x12, 0x22, 0x01, 0x02, 0xee, 0xee, 0xe2,
0x01, 0x11, 0xee, 0xee, 0xe0, 0x11, 0x12, 0x21, 0x01, 0x11, 0xee, 0x01,
0x11, 0x22, 0x22, 0x23, 0xee, 0x30, 0x11, 0x12, 0x21, 0x11, 0x02, 0xe3,
0x01, 0x10, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x12, 0x21, 0x10, 0x13,
0xee, 0x00, 0x00, 0x00, 0x00, 0x50, 0xee, 0x20, 0x11, 0x00, 0x11, 0x11,
0x03, 0xe1, 0x11, 0x00, 0x01, 0x11, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee,
0xee, 0xee, 0xe1, 0x11, 0x00, 0x01, 0x11, 0x1e, 0xee, 0x01, 0x11, 0x00,
0x01, 0x11, 0x12, 0xee, 0xe2, 0x01, 0x11, 0x00, 0x00, 0x2e, 0x20, 0xee,
0x00, 0x60, 0xe2, 0x01, 0x10, 0x01, 0x11, 0x12, 0x3e, 0x11, 0x10, 0x2e,
0x30, 0x11, 0x0e, 0xe1, 0x11, 0x10, 0x10, 0x11, 0x1e, 0xee, 0xee, 0xe2,
0x01, 0x11, 0xee, 0xee, 0xe0, 0x11, 0x10, 0x10, 0x11, 0x11, 0xee, 0x01,
0x11, 0x01, 0x00, 0x01, 0x2e, 0x30, 0x11, 0x00, 0x10, 0x11, 0x02, 0xe3,
0x01, 0x10, 0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x00, 0x10, 0x11, 0x2e,
0xee, 0x00, 0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe1, 0x11,
0x03, 0xe1, 0x11, 0x02, 0xe3, 0x11, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee,
0xee, 0xee, 0xe1, 0x11, 0x02, 0xe3, 0x11, 0x10, 0xee, 0x01, 0x11, 0x03,
0xe3, 0x11, 0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x60,
0xe2, 0x01, 0x11, 0xee, 0x11, 0x10, 0x3e, 0x11, 0x10, 0x2e, 0x30, 0x11,
0x0e, 0xe1, 0x11, 0x01, 0x22, 0x11, 0x12, 0xee, 0xee, 0xe2, 0x01, 0x11,
0xee, 0xee, 0xe0, 0x11, 0x02, 0x22, 0x11, 0x11, 0xee, 0x01, 0x10, 0x22,
0x21, 0x11, 0x1e, 0x30, 0x11, 0x02, 0x21, 0x11, 0x02, 0xe3, 0x01, 0x10,
0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x02, 0x21, 0x11, 0x23, 0xee, 0x00,
0x00, 0x00, 0x00, 0x4c, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1,
0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x11, 0x10, 0x2e, 0xee, 0xee, 0xee,
0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x13, 0xe3, 0x01,
0x11, 0x1e, 0xe2, 0x01, 0x11, 0x2e, 0x24, 0xee, 0x00, 0x60, 0xe2, 0x01,
0x11, 0xee, 0x01, 0x10, 0x3e, 0x11, 0x11, 0x2e, 0x30, 0x11, 0x0e, 0xe1,
0x11, 0x12, 0xe3, 0x11, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x11, 0xee, 0xee,
0xe0, 0x11, 0x13, 0xe2, 0x11, 0x11, 0xee, 0x01, 0x11, 0x3e, 0x21, 0x11,
0x1e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0x08, 0xee,
0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00, 0x00, 0x00,
0x00, 0x54, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe1, 0x11, 0x12,
0xe3, 0x01, 0x00, 0xee, 0x11, 0x11, 0x2e, 0xee, 0xee, 0xee, 0xe1, 0x11,
0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x11, 0x11, 0x00, 0x01, 0x10, 0x1e,
0xe2, 0x01, 0x11, 0x11, 0x00, 0x00, 0x01, 0x2e, 0x1c, 0xee, 0x00, 0x60,
0xe2, 0x01, 0x12, 0xee, 0x01, 0x00, 0x3e, 0x11, 0x11, 0x22, 0x20, 0x11,
0x0e, 0xe1, 0x11, 0x12, 0x22, 0x01, 0x10, 0xee, 0xee, 0xe2, 0x01, 0x12,
0xee, 0xee, 0xe0, 0x11, 0x12, 0x21, 0x01, 0x11, 0xee, 0x01, 0x11, 0x22,
0x10, 0x11, 0x1e, 0x30, 0x11, 0x12, 0x21, 0x11, 0x02, 0xe3, 0x01, 0x11,
0x08, 0xee, 0x00, 0x0e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02, 0xee, 0x00,
0x00, 0x00, 0x00, 0x38, 0xee, 0x20, 0x11, 0x1e, 0xe0, 0x11, 0x03, 0xe2,
0x11, 0x11, 0x00, 0x01, 0x02, 0xee, 0x21, 0x11, 0x10, 0x00, 0x00, 0x1e,
0xe1, 0x11, 0x02, 0xe3, 0x01, 0x10, 0xee, 0x01, 0x08, 0x11, 0x00, 0x08,
0x12, 0xee, 0xe3, 0x21, 0x08, 0x11, 0x02, 0x10, 0x02, 0x2e, 0x1c, 0xee,
0x00, 0x76, 0xe2, 0x01, 0x11, 0x10, 0x01, 0x02, 0x3e, 0x11, 0x11, 0x11,
0x01, 0x10, 0x0e, 0xe1, 0x11, 0x11, 0x10, 0x11, 0x00, 0xee, 0x10, 0x00,
0x01, 0x11, 0x10, 0x1e, 0xe0, 0x11, 0x11, 0x10, 0x11, 0x01, 0xee, 0x01,
0x11, 0x11, 0x01, 0x10, 0x1e, 0x30, 0x11, 0x11, 0x00, 0x10, 0x02, 0xe3,
0x01, 0x11, 0x10, 0x00, 0x00, 0x2e, 0x30, 0x11, 0x0e, 0xe1, 0x11, 0x02,
0xee, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xee, 0x20, 0x00, 0x1e, 0xe1, 0x00,
0x03, 0xee, 0x20, 0x00, 0x00, 0x00, 0x1e, 0xee, 0xe2, 0x00, 0x08, 0x00,
0x00, 0x12, 0x0e, 0xe2, 0x00, 0x02, 0xe3, 0x10, 0x00, 0xee, 0x10, 0x00,
0x08, 0x00, 0x00, 0x08, 0x02, 0xee, 0xee, 0x30, 0x0a, 0x00, 0x02, 0x2e,
0x1c, 0xee, 0x02, 0xe2, 0x08, 0x00, 0x00, 0x12, 0x03, 0xee, 0xe2, 0x00,
0x00, 0x00, 0x01, 0xee, 0xe2, 0x00, 0x08, 0x00, 0x00, 0x06, 0x1e, 0xee,
0x20, 0x00, 0x08, 0x00, 0x00, 0x1e, 0x0e, 0xee, 0x10, 0x00, 0x00, 0x00,
0x2e, 0xee, 0x10, 0x00, 0x00, 0x00, 0x02, 0xee, 0xe3, 0x00, 0x08, 0x00,
0x00, 0x06, 0x2e, 0xee, 0x30, 0x00, 0x08, 0x00, 0x00, 0x10, 0x2e, 0x31,
0x00, 0x0e, 0xe2, 0x00, 0x02, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00,
0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x00, 0xe6, 0xee, 0x00, 0x01};
unsigned int splash_bmp_len = 3828;
0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0x00, 0x08,
0x33, 0x44, 0x44, 0x47, 0x0a, 0x33, 0x00, 0x06, 0xd5, 0x44, 0x7d, 0x00,
0x0a, 0x33, 0x00, 0x0a, 0x44, 0x44, 0x48, 0x33, 0x3a, 0x00, 0x06, 0x44,
0x02, 0xd3, 0x12, 0x33, 0x00, 0x08, 0xa4, 0x44, 0x45, 0x9d, 0x08, 0x33,
0x00, 0x08, 0xc4, 0x44, 0x45, 0xc3, 0x10, 0x33, 0x00, 0x08, 0x34, 0x44,
0x44, 0x6d, 0x16, 0x33, 0x00, 0x08, 0x34, 0x44, 0x44, 0x6d, 0x08, 0x33,
0x02, 0xd5, 0x1a, 0x44, 0x00, 0x0c, 0x45, 0xc3, 0x33, 0x44, 0x44, 0x47,
0x0a, 0x33, 0x00, 0x06, 0xd5, 0x44, 0x7d, 0x00, 0x0a, 0x33, 0x00, 0x08,
0x44, 0x44, 0x48, 0x33, 0x00, 0x00, 0x00, 0x0a, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x00, 0x06, 0x33, 0x00, 0x0a, 0xd4, 0x46, 0x89, 0x98, 0xb3, 0x00,
0x08, 0x33, 0x00, 0x12, 0x49, 0x99, 0x94, 0x33, 0x3d, 0x79, 0x99, 0x99,
0x73, 0x00, 0x10, 0x33, 0x00, 0x0a, 0x3d, 0x47, 0x99, 0x95, 0xa3, 0x00,
0x08, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08,
0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c,
0x06, 0x33, 0x00, 0x06, 0xb4, 0x56, 0x89, 0x00, 0x18, 0x99, 0x00, 0x0e,
0x97, 0xa3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x00, 0x06, 0x33, 0x00, 0x0a,
0xd4, 0x46, 0x89, 0x98, 0xb3, 0x00, 0x08, 0x33, 0x00, 0x08, 0x49, 0x99,
0x94, 0x33, 0x00, 0x00, 0x00, 0x0a, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x00,
0x06, 0x33, 0x02, 0x55, 0x06, 0x99, 0x02, 0x9b, 0x08, 0x33, 0x00, 0x12,
0x49, 0x99, 0x94, 0x33, 0x33, 0xb9, 0x99, 0x99, 0x8d, 0x00, 0x10, 0x33,
0x00, 0x0a, 0x37, 0x69, 0x99, 0x74, 0xd3, 0x00, 0x08, 0x33, 0x00, 0x08,
0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c,
0x16, 0x33, 0x00, 0x10, 0x34, 0x99, 0x99, 0x7c, 0x33, 0x33, 0x3a, 0x47,
0x1c, 0x99, 0x00, 0x0e, 0x97, 0xa3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x00,
0x06, 0x33, 0x02, 0x55, 0x06, 0x99, 0x02, 0x9b, 0x08, 0x33, 0x00, 0x08,
0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x33, 0x33, 0x3a, 0x48, 0x00, 0x06, 0x99, 0x02, 0x98, 0x02, 0xd3,
0x06, 0x33, 0x00, 0x12, 0x49, 0x99, 0x94, 0x33, 0x33, 0xd7, 0x99, 0x99,
0x98, 0x00, 0x10, 0x33, 0x00, 0x08, 0xd4, 0x89, 0x99, 0x5a, 0x0a, 0x33,
0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x7c, 0x16, 0x33, 0x00, 0x10, 0x34, 0x99, 0x99, 0x7c, 0x33, 0x33,
0xb4, 0x79, 0x1c, 0x99, 0x00, 0x16, 0x97, 0xa3, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x33, 0x33, 0x3a, 0x48, 0x00, 0x06, 0x99, 0x02, 0x98, 0x02, 0xd3,
0x06, 0x33, 0x00, 0x08, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x12,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x33, 0xd4, 0x79, 0x00, 0x08, 0x99,
0x02, 0x73, 0x06, 0x33, 0x00, 0x0c, 0x49, 0x99, 0x94, 0x33, 0x33, 0x3b,
0x06, 0x99, 0x02, 0xc3, 0x0e, 0x33, 0x00, 0x08, 0x56, 0x99, 0x97, 0x4d,
0x0a, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08,
0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c,
0x33, 0x3d, 0x47, 0x00, 0x1e, 0x99, 0x00, 0x16, 0x97, 0xa3, 0x33, 0x49,
0x99, 0x95, 0xd3, 0x33, 0x33, 0xd4, 0x79, 0x00, 0x08, 0x99, 0x02, 0x73,
0x06, 0x33, 0x00, 0x08, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x10,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x33, 0xa5, 0x0a, 0x99, 0x02, 0x8d,
0x06, 0x33, 0x00, 0x14, 0x49, 0x99, 0x94, 0x33, 0x33, 0x3d, 0x79, 0x99,
0x99, 0x73, 0x0c, 0x33, 0x00, 0x0a, 0x3c, 0x48, 0x99, 0x95, 0xa3, 0x00,
0x0a, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x97, 0xa3, 0x10, 0x33, 0x00, 0x08,
0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c,
0x33, 0x37, 0x69, 0x00, 0x1e, 0x99, 0x00, 0x14, 0x97, 0xa3, 0x33, 0x49,
0x99, 0x95, 0xd3, 0x33, 0x33, 0xa5, 0x0a, 0x99, 0x02, 0x8d, 0x06, 0x33,
0x00, 0x08, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x1c, 0x33, 0x49,
0x99, 0x95, 0xd3, 0x33, 0x3d, 0x47, 0x99, 0x97, 0x89, 0x99, 0x99, 0x97,
0x06, 0x33, 0x00, 0x06, 0x49, 0x99, 0x94, 0x00, 0x06, 0x33, 0x00, 0x08,
0xb9, 0x99, 0x99, 0x9d, 0x0c, 0x33, 0x00, 0x0a, 0x34, 0x69, 0x99, 0x74,
0xd3, 0x00, 0x0a, 0x33, 0x00, 0x08, 0xc7, 0x99, 0x98, 0xa3, 0x10, 0x33,
0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x12, 0x34, 0x99,
0x99, 0x7c, 0x33, 0x34, 0x89, 0x99, 0x64, 0x00, 0x1c, 0x44, 0x00, 0x1e,
0xa3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x3d, 0x47, 0x99, 0x97, 0x89,
0x99, 0x99, 0x97, 0x00, 0x06, 0x33, 0x00, 0x08, 0x49, 0x99, 0x94, 0x33,
0x00, 0x00, 0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x3c, 0x59,
0x99, 0x95, 0xbc, 0x99, 0x99, 0x98, 0xd3, 0x33, 0x33, 0x49, 0x99, 0x94,
0x06, 0x33, 0x02, 0xd7, 0x06, 0x99, 0x02, 0x74, 0x0c, 0x44, 0x00, 0x06,
0x99, 0x99, 0x4b, 0x00, 0x0c, 0x33, 0x00, 0x08, 0xb7, 0x99, 0x99, 0xa3,
0x10, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x16, 0x33, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0x33, 0xd5, 0x99, 0x99, 0x6d, 0x00, 0x20, 0x33,
0x00, 0x28, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x3c, 0x59, 0x99, 0x95, 0xbc,
0x99, 0x99, 0x98, 0xd3, 0x33, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00,
0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x34, 0x79, 0x99, 0x84,
0xdd, 0x89, 0x99, 0x99, 0x83, 0x33, 0x33, 0x49, 0x99, 0x94, 0x06, 0x33,
0x02, 0x3b, 0x16, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0x33, 0x00, 0x0a,
0x3d, 0x47, 0x99, 0x99, 0x9d, 0x00, 0x10, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x9d, 0x16, 0x33, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0x33, 0xd6,
0x99, 0x99, 0x6d, 0x00, 0x20, 0x33, 0x00, 0x28, 0x49, 0x99, 0x95, 0xd3,
0x33, 0x34, 0x79, 0x99, 0x84, 0xdd, 0x89, 0x99, 0x99, 0x83, 0x33, 0x33,
0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x28, 0x33, 0x49, 0x99, 0x95,
0xd3, 0x33, 0xc4, 0x99, 0x99, 0x67, 0x33, 0x79, 0x99, 0x99, 0x8d, 0x33,
0x33, 0x49, 0x99, 0x94, 0x06, 0x33, 0x02, 0x3d, 0x02, 0x79, 0x14, 0x99,
0x02, 0x94, 0x02, 0xc3, 0x0a, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x99,
0x98, 0x00, 0x10, 0x33, 0x02, 0x34, 0x06, 0x99, 0x02, 0x74, 0x16, 0x44,
0x00, 0x10, 0x99, 0x99, 0x7c, 0x33, 0xd6, 0x99, 0x99, 0x9d, 0x20, 0x33,
0x00, 0x28, 0x49, 0x99, 0x95, 0xd3, 0x33, 0xc4, 0x99, 0x99, 0x67, 0x33,
0x79, 0x99, 0x99, 0x8d, 0x33, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00,
0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x57, 0x99, 0x99, 0x4d,
0x33, 0xd8, 0x99, 0x99, 0x98, 0x33, 0x33, 0x49, 0x99, 0x94, 0x08, 0x33,
0x02, 0xc9, 0x14, 0x99, 0x02, 0x74, 0x0c, 0x33, 0x02, 0x84, 0x02, 0x89,
0x06, 0x99, 0x02, 0xa3, 0x0e, 0x33, 0x02, 0x34, 0x22, 0x99, 0x00, 0x06,
0x7c, 0x33, 0xd6, 0x00, 0x06, 0x99, 0x02, 0x64, 0x18, 0x44, 0x00, 0x2e,
0x45, 0xc3, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x33, 0x57, 0x99, 0x99, 0x4d,
0x33, 0xd8, 0x99, 0x99, 0x98, 0x33, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x3d, 0x48, 0x99,
0x97, 0x43, 0x33, 0x37, 0x99, 0x99, 0x97, 0xd3, 0x33, 0x49, 0x99, 0x94,
0x08, 0x33, 0x02, 0x37, 0x14, 0x99, 0x02, 0x4c, 0x0a, 0x33, 0x02, 0x3b,
0x02, 0x48, 0x08, 0x99, 0x02, 0x9b, 0x0e, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x3d, 0x48, 0x99, 0x97, 0x43, 0x33, 0x37,
0x99, 0x99, 0x97, 0xd3, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00,
0x00, 0x28, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x35, 0x69, 0x99, 0x95, 0xc3,
0x33, 0x3c, 0x89, 0x99, 0x99, 0xb3, 0x33, 0x49, 0x99, 0x94, 0x08, 0x33,
0x02, 0x3c, 0x12, 0x99, 0x02, 0x97, 0x02, 0x43, 0x0a, 0x33, 0x02, 0xd4,
0x02, 0x79, 0x0a, 0x99, 0x02, 0xd3, 0x0c, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x2e, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x35, 0x69, 0x99, 0x95, 0xc3, 0x33, 0x3c,
0x89, 0x99, 0x99, 0xb3, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00,
0x00, 0x12, 0x33, 0x49, 0x99, 0x95, 0xd3, 0xd4, 0x89, 0x99, 0x74, 0x00,
0x06, 0x33, 0x00, 0x10, 0x79, 0x99, 0x99, 0x7d, 0x33, 0x49, 0x99, 0x94,
0x0a, 0x33, 0x00, 0x16, 0x79, 0x99, 0x99, 0x64, 0x44, 0x44, 0x89, 0x99,
0x99, 0x94, 0xc3, 0x00, 0x08, 0x33, 0x00, 0x0a, 0x3d, 0x46, 0x99, 0x98,
0x59, 0x00, 0x06, 0x99, 0x02, 0x8d, 0x0c, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x16, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0xd4, 0x89, 0x99, 0x74, 0x00, 0x06, 0x33,
0x00, 0x12, 0x79, 0x99, 0x99, 0x7d, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x95, 0xd3, 0x76, 0x99, 0x99,
0x6a, 0x00, 0x06, 0x33, 0x00, 0x10, 0xa9, 0x99, 0x99, 0x9b, 0x33, 0x49,
0x99, 0x94, 0x0a, 0x33, 0x00, 0x14, 0xc9, 0x99, 0x99, 0x9c, 0x33, 0x33,
0x3b, 0x99, 0x99, 0x74, 0x0a, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x95,
0x7d, 0x00, 0x06, 0x99, 0x02, 0x9a, 0x0c, 0x33, 0x02, 0x34, 0x22, 0x99,
0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00, 0x20, 0x99, 0x00, 0x16, 0x97, 0xa3,
0x33, 0x49, 0x99, 0x95, 0xd3, 0x76, 0x99, 0x99, 0x6a, 0x00, 0x06, 0x33,
0x00, 0x12, 0xa9, 0x99, 0x99, 0x9b, 0x33, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x95, 0x3d, 0x48, 0x99, 0x98,
0x4d, 0x00, 0x06, 0x33, 0x00, 0x10, 0xd7, 0x99, 0x99, 0x97, 0xd3, 0x49,
0x99, 0x94, 0x0a, 0x33, 0x00, 0x14, 0x37, 0x99, 0x99, 0x97, 0x33, 0x33,
0xc4, 0x89, 0x99, 0x4c, 0x0a, 0x33, 0x00, 0x0c, 0x84, 0x89, 0x99, 0x64,
0x33, 0xd9, 0x06, 0x99, 0x02, 0xa3, 0x0a, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x64, 0x16, 0x44, 0x06, 0x99, 0x00, 0x06, 0x7c, 0x33, 0xd6, 0x00,
0x20, 0x99, 0x00, 0x16, 0x97, 0xa3, 0x33, 0x49, 0x99, 0x95, 0x3d, 0x48,
0x99, 0x98, 0x4d, 0x00, 0x06, 0x33, 0x00, 0x12, 0xd7, 0x99, 0x99, 0x97,
0xd3, 0x49, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x00, 0x12, 0x33, 0x49,
0x99, 0x96, 0x3b, 0x69, 0x99, 0x96, 0x53, 0x00, 0x06, 0x33, 0x02, 0x38,
0x06, 0x99, 0x00, 0x08, 0xc3, 0x49, 0x99, 0x94, 0x0a, 0x33, 0x02, 0x3c,
0x06, 0x99, 0x00, 0x0c, 0xb3, 0x33, 0x46, 0x99, 0x96, 0x43, 0x08, 0x33,
0x00, 0x0e, 0x3b, 0x48, 0x99, 0x97, 0x4d, 0x33, 0x3d, 0x00, 0x06, 0x99,
0x02, 0x9c, 0x0a, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33,
0x00, 0x12, 0x39, 0x99, 0x99, 0x7c, 0x33, 0xd6, 0x99, 0x99, 0x54, 0x00,
0x1c, 0x44, 0x00, 0x14, 0xa3, 0x33, 0x49, 0x99, 0x96, 0x3b, 0x69, 0x99,
0x96, 0x53, 0x06, 0x33, 0x02, 0x38, 0x06, 0x99, 0x00, 0x0a, 0xc3, 0x49,
0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x00, 0x12, 0x33, 0x49, 0x99, 0x98,
0xd5, 0x79, 0x99, 0x94, 0xd3, 0x00, 0x06, 0x33, 0x00, 0x10, 0x3d, 0x89,
0x99, 0x99, 0x73, 0x49, 0x99, 0x94, 0x0c, 0x33, 0x00, 0x12, 0x79, 0x99,
0x99, 0x8d, 0x3a, 0x59, 0x99, 0x84, 0xd3, 0x00, 0x08, 0x33, 0x00, 0x10,
0xd4, 0x79, 0x99, 0x84, 0xb3, 0x33, 0x33, 0xb9, 0x06, 0x99, 0x02, 0xd3,
0x08, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33, 0x00, 0x12,
0x34, 0x99, 0x99, 0x7c, 0x33, 0xd6, 0x99, 0x99, 0x5d, 0x00, 0x20, 0x33,
0x00, 0x10, 0x49, 0x99, 0x98, 0xd5, 0x79, 0x99, 0x94, 0xd3, 0x06, 0x33,
0x00, 0x12, 0x3d, 0x89, 0x99, 0x99, 0x73, 0x49, 0x99, 0x94, 0x33, 0x00,
0x00, 0x00, 0x00, 0x10, 0x33, 0x49, 0x99, 0x99, 0xa6, 0x99, 0x99, 0x74,
0x0a, 0x33, 0x00, 0x0e, 0x79, 0x99, 0x99, 0x9c, 0x59, 0x99, 0x94, 0x00,
0x0c, 0x33, 0x00, 0x10, 0xc9, 0x99, 0x99, 0x9a, 0xd4, 0x79, 0x99, 0x65,
0x08, 0x33, 0x00, 0x0a, 0x3d, 0x46, 0x99, 0x98, 0x48, 0x00, 0x06, 0x33,
0x02, 0x3a, 0x06, 0x99, 0x02, 0x83, 0x08, 0x33, 0x00, 0x08, 0x34, 0x99,
0x99, 0x6d, 0x16, 0x33, 0x00, 0x12, 0x34, 0x99, 0x99, 0x7c, 0x33, 0xd6,
0x99, 0x99, 0x93, 0x00, 0x20, 0x33, 0x00, 0x0e, 0x49, 0x99, 0x99, 0xa6,
0x99, 0x99, 0x74, 0x00, 0x0a, 0x33, 0x00, 0x10, 0x79, 0x99, 0x99, 0x9c,
0x59, 0x99, 0x94, 0x33, 0x00, 0x00, 0x00, 0x10, 0x33, 0x49, 0x99, 0x99,
0x98, 0x99, 0x99, 0x5c, 0x0a, 0x33, 0x02, 0xd8, 0x06, 0x99, 0x00, 0x06,
0x79, 0x99, 0x84, 0x00, 0x0c, 0x33, 0x02, 0x37, 0x06, 0x99, 0x00, 0x08,
0x86, 0x99, 0x98, 0x4d, 0x08, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x95,
0x53, 0x00, 0x08, 0x33, 0x00, 0x08, 0x89, 0x99, 0x99, 0x9a, 0x08, 0x33,
0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33, 0x00, 0x0c, 0x34, 0x99,
0x99, 0x7c, 0x33, 0x36, 0x06, 0x99, 0x02, 0x54, 0x18, 0x44, 0x00, 0x14,
0x45, 0xc3, 0x33, 0x49, 0x99, 0x99, 0x98, 0x99, 0x99, 0x5c, 0x0a, 0x33,
0x02, 0xd8, 0x06, 0x99, 0x00, 0x08, 0x79, 0x99, 0x84, 0x33, 0x00, 0x00,
0x02, 0x33, 0x02, 0x59, 0x08, 0x99, 0x02, 0x97, 0x02, 0x4d, 0x0a, 0x33,
0x02, 0x36, 0x0a, 0x99, 0x02, 0x75, 0x0c, 0x33, 0x02, 0x3c, 0x0a, 0x99,
0x02, 0x96, 0x02, 0x53, 0x08, 0x33, 0x00, 0x0a, 0x84, 0x89, 0x99, 0x64,
0xd3, 0x00, 0x08, 0x33, 0x02, 0xd9, 0x06, 0x99, 0x02, 0xb3, 0x06, 0x33,
0x00, 0x08, 0x34, 0x99, 0x99, 0x6d, 0x16, 0x33, 0x00, 0x0c, 0x34, 0x99,
0x99, 0x7c, 0x33, 0x36, 0x20, 0x99, 0x00, 0x08, 0x97, 0xa3, 0x33, 0x59,
0x08, 0x99, 0x02, 0x97, 0x02, 0x4d, 0x0a, 0x33, 0x02, 0x36, 0x0a, 0x99,
0x02, 0x75, 0x02, 0x33, 0x00, 0x00, 0x02, 0x33, 0x02, 0xa8, 0x08, 0x99,
0x02, 0x95, 0x02, 0xa3, 0x0a, 0x33, 0x02, 0x3c, 0x0a, 0x99, 0x02, 0x5b,
0x0e, 0x33, 0x02, 0x89, 0x08, 0x99, 0x02, 0x84, 0x02, 0xd3, 0x06, 0x33,
0x00, 0x0a, 0x3b, 0x48, 0x99, 0x97, 0x4c, 0x00, 0x0a, 0x33, 0x02, 0x3d,
0x06, 0x99, 0x02, 0x9d, 0x06, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x6d,
0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c, 0x33, 0x3d, 0x89, 0x00,
0x1e, 0x99, 0x00, 0x08, 0x97, 0xa3, 0x33, 0xa8, 0x08, 0x99, 0x02, 0x95,
0x02, 0xa3, 0x0a, 0x33, 0x02, 0x3c, 0x0a, 0x99, 0x02, 0x5b, 0x02, 0x33,
0x00, 0x00, 0x02, 0x33, 0x02, 0xd7, 0x08, 0x99, 0x02, 0x64, 0x0e, 0x33,
0x02, 0x79, 0x06, 0x99, 0x02, 0x96, 0x02, 0x4d, 0x0e, 0x33, 0x02, 0xd8,
0x06, 0x99, 0x02, 0x98, 0x02, 0x48, 0x08, 0x33, 0x00, 0x0a, 0xd4, 0x79,
0x99, 0x84, 0xb3, 0x00, 0x0c, 0x33, 0x00, 0x16, 0xb9, 0x99, 0x99, 0x98,
0xd3, 0x33, 0x33, 0x34, 0x99, 0x99, 0x6d, 0x00, 0x16, 0x33, 0x00, 0x0c,
0x34, 0x99, 0x99, 0x7c, 0x33, 0x33, 0x20, 0x99, 0x00, 0x08, 0x97, 0xa3,
0x33, 0xd7, 0x08, 0x99, 0x02, 0x64, 0x0e, 0x33, 0x02, 0x79, 0x06, 0x99,
0x00, 0x06, 0x96, 0x4d, 0x33, 0x00, 0x00, 0x00, 0x02, 0x33, 0x02, 0x3c,
0x06, 0x99, 0x02, 0x96, 0x02, 0x4d, 0x0e, 0x33, 0x02, 0xd9, 0x06, 0x99,
0x02, 0x74, 0x02, 0xc3, 0x0e, 0x33, 0x02, 0x3d, 0x06, 0x99, 0x02, 0x85,
0x02, 0x53, 0x06, 0x33, 0x00, 0x0a, 0x3d, 0x46, 0x99, 0x99, 0x57, 0x00,
0x0e, 0x33, 0x02, 0x39, 0x06, 0x99, 0x00, 0x0e, 0xa3, 0x33, 0x33, 0x34,
0x99, 0x99, 0x6d, 0x00, 0x16, 0x33, 0x00, 0x0e, 0x34, 0x99, 0x99, 0x7c,
0x33, 0x33, 0x3a, 0x00, 0x1e, 0x99, 0x00, 0x08, 0x97, 0xa3, 0x33, 0x3c,
0x06, 0x99, 0x02, 0x96, 0x02, 0x4d, 0x0e, 0x33, 0x02, 0xd9, 0x06, 0x99,
0x00, 0x06, 0x74, 0xc3, 0x33, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x33, 0x33,
0xc8, 0x99, 0x96, 0x44, 0xd3, 0x00, 0x0e, 0x33, 0x00, 0x0a, 0x3d, 0x79,
0x99, 0x76, 0x4b, 0x00, 0x12, 0x33, 0x00, 0x0a, 0xc7, 0x99, 0x96, 0x45,
0xd3, 0x00, 0x06, 0x33, 0x00, 0x0a, 0x35, 0x59, 0x99, 0x96, 0x43, 0x00,
0x10, 0x33, 0x00, 0x14, 0x89, 0x99, 0x99, 0x9a, 0x33, 0x33, 0x34, 0x99,
0x99, 0x6d, 0x16, 0x33, 0x00, 0x08, 0x34, 0x99, 0x99, 0x7c, 0x06, 0x33,
0x02, 0xa6, 0x1c, 0x99, 0x00, 0x12, 0x97, 0xa3, 0x33, 0x33, 0xc8, 0x99,
0x96, 0x44, 0xd3, 0x00, 0x0e, 0x33, 0x00, 0x0e, 0x3d, 0x79, 0x99, 0x76,
0x4b, 0x33, 0x33, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x33, 0x33, 0x3d, 0x84,
0x44, 0xc3, 0x12, 0x33, 0x00, 0x08, 0xdc, 0x44, 0x4a, 0xd3, 0x12, 0x33,
0x00, 0x08, 0x3d, 0xa4, 0x44, 0xd3, 0x08, 0x33, 0x02, 0xc8, 0x06, 0x44,
0x02, 0xd3, 0x10, 0x33, 0x02, 0xd4, 0x06, 0x44, 0x00, 0x0c, 0xa3, 0x33,
0x38, 0x44, 0x44, 0x4d, 0x16, 0x33, 0x00, 0x08, 0x38, 0x44, 0x44, 0x4c,
0x06, 0x33, 0x02, 0x3d, 0x02, 0xc4, 0x1c, 0x44, 0x00, 0x0e, 0xa3, 0x33,
0x33, 0x3d, 0x84, 0x44, 0xc3, 0x00, 0x12, 0x33, 0x00, 0x0c, 0xdc, 0x44,
0x4a, 0xd3, 0x33, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0x00, 0x1e, 0x33, 0x20, 0x00, 0x23, 0x30, 0x00, 0x13, 0x31,
0x00, 0x02, 0x33, 0x00, 0x01, 0x33, 0x32, 0x00, 0x08, 0x00, 0x00, 0x26,
0x13, 0x31, 0x00, 0x02, 0x33, 0x00, 0x01, 0x33, 0x00, 0x00, 0x13, 0x33,
0x00, 0x00, 0x13, 0x32, 0x00, 0x00, 0x23, 0x00, 0x24, 0x33, 0x00, 0x06,
0x32, 0x00, 0x02, 0x00, 0x08, 0x33, 0x02, 0x32, 0x06, 0x00, 0x00, 0x30,
0x01, 0x33, 0x31, 0x00, 0x02, 0x33, 0x00, 0x01, 0x33, 0x33, 0x32, 0x00,
0x02, 0x33, 0x33, 0x30, 0x00, 0x13, 0x32, 0x00, 0x02, 0x33, 0x00, 0x01,
0x08, 0x33, 0x00, 0x10, 0x30, 0x00, 0x13, 0x31, 0x00, 0x02, 0x33, 0x30,
0x08, 0x00, 0x00, 0x10, 0x23, 0x30, 0x00, 0x13, 0x31, 0x00, 0x02, 0x33,
0x00, 0x00, 0x00, 0x20, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x10, 0x10, 0x06, 0x00, 0x00, 0x26,
0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01, 0x11, 0x03, 0x33,
0x01, 0x11, 0x13, 0x32, 0x01, 0x11, 0x23, 0x00, 0x24, 0x33, 0x00, 0x06,
0x32, 0x01, 0x11, 0x00, 0x08, 0x33, 0x00, 0x38, 0x10, 0x10, 0x00, 0x11,
0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x33, 0x32, 0x01,
0x11, 0x33, 0x33, 0x30, 0x11, 0x03, 0x32, 0x01, 0x11, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x12, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00,
0x10, 0x00, 0x06, 0x00, 0x00, 0x10, 0x23, 0x30, 0x11, 0x03, 0x31, 0x11,
0x02, 0x33, 0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11,
0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00,
0x06, 0x33, 0x00, 0x24, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01,
0x11, 0x13, 0x33, 0x01, 0x10, 0x13, 0x32, 0x01, 0x11, 0x23, 0x24, 0x33,
0x00, 0x06, 0x32, 0x01, 0x11, 0x00, 0x08, 0x33, 0x00, 0x38, 0x11, 0x10,
0x23, 0x31, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x33,
0x32, 0x01, 0x11, 0x33, 0x33, 0x30, 0x11, 0x03, 0x32, 0x01, 0x11, 0x33,
0x01, 0x10, 0x08, 0x33, 0x00, 0x12, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02,
0x33, 0x01, 0x10, 0x00, 0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x23,
0x30, 0x11, 0x03, 0x31, 0x11, 0x12, 0x33, 0x01, 0x10, 0x33, 0x11, 0x10,
0x23, 0x00, 0x06, 0x33, 0x00, 0x28, 0x31, 0x11, 0x12, 0x33, 0x01, 0x00,
0x33, 0x01, 0x11, 0x11, 0x00, 0x01, 0x12, 0x33, 0x32, 0x01, 0x11, 0x11,
0x00, 0x23, 0x20, 0x33, 0x00, 0x06, 0x32, 0x01, 0x12, 0x00, 0x08, 0x33,
0x00, 0x38, 0x11, 0x10, 0x23, 0x30, 0x11, 0x03, 0x31, 0x11, 0x12, 0x33,
0x01, 0x00, 0x33, 0x33, 0x32, 0x01, 0x11, 0x33, 0x33, 0x30, 0x11, 0x13,
0x32, 0x01, 0x11, 0x33, 0x01, 0x11, 0x08, 0x33, 0x00, 0x12, 0x30, 0x11,
0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x00, 0x08, 0x33, 0x00, 0x0e,
0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x22,
0x33, 0x20, 0x11, 0x11, 0x00, 0x11, 0x03, 0x31, 0x11, 0x11, 0x00, 0x01,
0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33, 0x00, 0x10, 0x31, 0x11,
0x11, 0x00, 0x01, 0x13, 0x33, 0x01, 0x08, 0x11, 0x00, 0x10, 0x12, 0x33,
0x32, 0x01, 0x11, 0x11, 0x10, 0x23, 0x20, 0x33, 0x00, 0x60, 0x32, 0x01,
0x11, 0x10, 0x00, 0x03, 0x33, 0x11, 0x10, 0x23, 0x30, 0x11, 0x03, 0x31,
0x11, 0x12, 0x22, 0x01, 0x02, 0x33, 0x33, 0x32, 0x01, 0x11, 0x33, 0x33,
0x30, 0x11, 0x12, 0x21, 0x01, 0x11, 0x33, 0x01, 0x11, 0x22, 0x22, 0x23,
0x33, 0x30, 0x11, 0x12, 0x21, 0x11, 0x02, 0x33, 0x01, 0x10, 0x08, 0x33,
0x00, 0x0e, 0x30, 0x11, 0x12, 0x21, 0x10, 0x13, 0x33, 0x00, 0x00, 0x00,
0x00, 0x22, 0x33, 0x20, 0x11, 0x00, 0x11, 0x11, 0x03, 0x31, 0x11, 0x00,
0x01, 0x11, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33, 0x00, 0x28,
0x31, 0x11, 0x00, 0x01, 0x11, 0x13, 0x33, 0x01, 0x11, 0x00, 0x01, 0x11,
0x12, 0x33, 0x32, 0x01, 0x11, 0x00, 0x00, 0x23, 0x20, 0x33, 0x00, 0x60,
0x32, 0x01, 0x10, 0x01, 0x11, 0x12, 0x33, 0x11, 0x10, 0x23, 0x30, 0x11,
0x03, 0x31, 0x11, 0x10, 0x10, 0x11, 0x13, 0x33, 0x33, 0x32, 0x01, 0x11,
0x33, 0x33, 0x30, 0x11, 0x10, 0x10, 0x11, 0x11, 0x33, 0x01, 0x11, 0x01,
0x00, 0x01, 0x23, 0x30, 0x11, 0x00, 0x10, 0x11, 0x02, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x00, 0x10, 0x11, 0x23, 0x33, 0x00,
0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x31, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x11, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33,
0x00, 0x24, 0x31, 0x11, 0x02, 0x33, 0x11, 0x10, 0x33, 0x01, 0x11, 0x03,
0x33, 0x11, 0x11, 0x13, 0x32, 0x01, 0x11, 0x23, 0x24, 0x33, 0x00, 0x60,
0x32, 0x01, 0x11, 0x33, 0x11, 0x10, 0x33, 0x11, 0x10, 0x23, 0x30, 0x11,
0x03, 0x31, 0x11, 0x01, 0x22, 0x11, 0x12, 0x33, 0x33, 0x32, 0x01, 0x11,
0x33, 0x33, 0x30, 0x11, 0x02, 0x22, 0x11, 0x11, 0x33, 0x01, 0x10, 0x22,
0x21, 0x11, 0x13, 0x30, 0x11, 0x02, 0x21, 0x11, 0x02, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x02, 0x21, 0x11, 0x23, 0x33, 0x00,
0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x11, 0x10, 0x23, 0x00, 0x06, 0x33,
0x00, 0x24, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01, 0x11, 0x13,
0x33, 0x01, 0x11, 0x13, 0x32, 0x01, 0x11, 0x23, 0x24, 0x33, 0x00, 0x60,
0x32, 0x01, 0x11, 0x33, 0x01, 0x10, 0x33, 0x11, 0x11, 0x23, 0x30, 0x11,
0x03, 0x31, 0x11, 0x12, 0x33, 0x11, 0x10, 0x33, 0x33, 0x32, 0x01, 0x11,
0x33, 0x33, 0x30, 0x11, 0x13, 0x32, 0x11, 0x11, 0x33, 0x01, 0x11, 0x33,
0x21, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10,
0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00,
0x00, 0x00, 0x00, 0x22, 0x33, 0x20, 0x11, 0x13, 0x30, 0x11, 0x03, 0x31,
0x11, 0x12, 0x33, 0x01, 0x00, 0x33, 0x11, 0x11, 0x23, 0x00, 0x06, 0x33,
0x00, 0x2c, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01, 0x11, 0x11,
0x00, 0x01, 0x10, 0x13, 0x32, 0x01, 0x11, 0x11, 0x00, 0x00, 0x01, 0x23,
0x1c, 0x33, 0x00, 0x60, 0x32, 0x01, 0x12, 0x33, 0x01, 0x00, 0x33, 0x11,
0x11, 0x22, 0x20, 0x11, 0x03, 0x31, 0x11, 0x12, 0x22, 0x01, 0x10, 0x33,
0x33, 0x32, 0x01, 0x12, 0x33, 0x33, 0x30, 0x11, 0x12, 0x21, 0x01, 0x11,
0x33, 0x01, 0x11, 0x22, 0x10, 0x11, 0x13, 0x30, 0x11, 0x12, 0x21, 0x11,
0x02, 0x33, 0x01, 0x11, 0x08, 0x33, 0x00, 0x0e, 0x30, 0x11, 0x03, 0x31,
0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x38, 0x33, 0x20, 0x11, 0x13,
0x30, 0x11, 0x03, 0x32, 0x11, 0x11, 0x00, 0x01, 0x02, 0x33, 0x21, 0x11,
0x10, 0x00, 0x00, 0x13, 0x31, 0x11, 0x02, 0x33, 0x01, 0x10, 0x33, 0x01,
0x08, 0x11, 0x00, 0x08, 0x12, 0x33, 0x33, 0x21, 0x08, 0x11, 0x02, 0x10,
0x02, 0x23, 0x1c, 0x33, 0x00, 0x0e, 0x32, 0x01, 0x11, 0x10, 0x01, 0x02,
0x33, 0x00, 0x06, 0x11, 0x00, 0x62, 0x01, 0x10, 0x03, 0x31, 0x11, 0x11,
0x10, 0x11, 0x00, 0x33, 0x10, 0x00, 0x01, 0x11, 0x10, 0x13, 0x30, 0x11,
0x11, 0x10, 0x11, 0x01, 0x33, 0x01, 0x11, 0x11, 0x01, 0x10, 0x13, 0x30,
0x11, 0x11, 0x00, 0x10, 0x02, 0x33, 0x01, 0x11, 0x10, 0x00, 0x00, 0x23,
0x30, 0x11, 0x03, 0x31, 0x11, 0x02, 0x33, 0x00, 0x00, 0x00, 0x00, 0x12,
0x33, 0x20, 0x00, 0x13, 0x31, 0x00, 0x03, 0x33, 0x20, 0x00, 0x06, 0x00,
0x00, 0x06, 0x13, 0x33, 0x32, 0x00, 0x08, 0x00, 0x00, 0x12, 0x03, 0x32,
0x00, 0x02, 0x33, 0x10, 0x00, 0x33, 0x10, 0x00, 0x08, 0x00, 0x00, 0x08,
0x02, 0x33, 0x33, 0x30, 0x0a, 0x00, 0x02, 0x23, 0x1c, 0x33, 0x02, 0x32,
0x08, 0x00, 0x00, 0x06, 0x03, 0x33, 0x32, 0x00, 0x06, 0x00, 0x00, 0x06,
0x01, 0x33, 0x32, 0x00, 0x08, 0x00, 0x00, 0x06, 0x13, 0x33, 0x20, 0x00,
0x08, 0x00, 0x00, 0x06, 0x03, 0x33, 0x10, 0x00, 0x06, 0x00, 0x00, 0x06,
0x23, 0x33, 0x10, 0x00, 0x06, 0x00, 0x00, 0x06, 0x02, 0x33, 0x33, 0x00,
0x08, 0x00, 0x00, 0x06, 0x23, 0x33, 0x30, 0x00, 0x08, 0x00, 0x00, 0x10,
0x23, 0x31, 0x00, 0x03, 0x32, 0x00, 0x02, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33, 0x00, 0x00, 0xe6, 0x33,
0x00, 0x01};
unsigned int splash_new__new_t_len = 3854;
@@ -20,45 +20,26 @@
* Boston, MA 02110-1301, USA.
*/
#include "acars_app.hpp"
#include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp"
#include "file_path.hpp"
#include "audio.hpp"
#include "acars_app.hpp"
using namespace portapack;
using namespace acars;
#include "string_format.hpp"
#include "utility.hpp"
void ACARSLogger::log_raw_data(const acars::Packet& packet, const uint32_t frequency) {
(void)frequency;
std::string entry{}; //= "Raw: F:" + to_string_dec_uint(frequency) + "Hz ";
entry.reserve(256);
namespace ui::external_app::acars_rx {
// Raw hex dump of all the bytes
// for (size_t c = 0; c < packet.length(); c += 32)
// entry += to_string_hex(packet.read(c, 32), 8) + " ";
for (size_t c = 0; c < 256; c += 32)
entry += to_string_bin(packet.read(c, 32), 32);
log_file.write_entry(packet.received_at(), entry);
void ACARSLogger::log_str(std::string msg) {
log_file.write_entry(msg);
}
/*void ACARSLogger::log_decoded(
const acars::Packet& packet,
const std::string text) {
log_file.write_entry(packet.timestamp(), text);
}*/
namespace ui {
ACARSAppView::ACARSAppView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_acars);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&rssi,
&channel,
@@ -66,6 +47,7 @@ ACARSAppView::ACARSAppView(NavigationView& nav)
&field_lna,
&field_vga,
&field_frequency,
&field_volume,
&check_log,
&console});
@@ -79,6 +61,9 @@ ACARSAppView::ACARSAppView(NavigationView& nav)
logger = std::make_unique<ACARSLogger>();
if (logger)
logger->append(logs_dir / u"ACARS.TXT");
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
}
ACARSAppView::~ACARSAppView() {
@@ -90,29 +75,31 @@ void ACARSAppView::focus() {
field_frequency.focus();
}
void ACARSAppView::on_packet(const acars::Packet& packet) {
void ACARSAppView::on_packet(const ACARSPacketMessage* packet) {
std::string console_info;
/*if (!packet.is_valid()) {
console_info = to_string_datetime(packet.received_at(), HMS);
console_info += " INVALID";
console.writeln(console_info);
} else {
console_info = to_string_datetime(packet.received_at(), HMS);
console_info += ":" + to_string_bin(packet.read(0, 32), 32);
//console_info += " REG:" + packet.registration_number();
console.writeln(console_info);
}*/
packet_counter++;
if (packet_counter % 10 == 0)
console.writeln("Block #" + to_string_dec_uint(packet_counter));
// Log raw data whatever it contains
if (logger && logging)
logger->log_raw_data(packet, receiver_model.target_frequency());
if (packet->state == 255) {
// got a packet, parse it, and display
rtc::RTC datetime;
rtc_time::now(datetime);
// todo parity error recovery
console_info = to_string_datetime(datetime, HMS);
console_info += ": ";
console_info += packet->message;
console.writeln(console_info);
// Log raw data whatever it contains
if (logger && logging)
logger->log_str(console_info);
} else {
// debug message arrived
console_info = "State: ";
console_info += to_string_dec_int(packet->state);
console_info += " lastbyte: ";
console_info += to_string_dec_uint(packet->message[0]);
console.writeln(console_info);
if (logger && logging)
logger->log_str(console_info);
}
}
} /* namespace ui */
} // namespace ui::external_app::acars_rx
@@ -31,23 +31,19 @@
#include "ui_rssi.hpp"
#include "log_file.hpp"
#include "acars_packet.hpp"
namespace ui::external_app::acars_rx {
class ACARSLogger {
public:
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
void log_raw_data(const acars::Packet& packet, const uint32_t frequency);
// void log_decoded(const acars::Packet& packet, const std::string text);
void log_str(std::string msg);
private:
LogFile log_file{};
};
namespace ui {
class ACARSAppView : public View {
public:
ACARSAppView(NavigationView& nav);
@@ -55,12 +51,12 @@ class ACARSAppView : public View {
void focus() override;
std::string title() const override { return "ACARS (WIP)"; };
std::string title() const override { return "ACARS"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{
131550000 /* frequency */,
131825000 /* frequency */,
1750000 /* bandwidth */,
2457600 /* sampling rate */
};
@@ -85,7 +81,7 @@ class ACARSAppView : public View {
{0 * 8, 0 * 8},
nav_};
Checkbox check_log{
{22 * 8, 21},
{16 * 8, 1 * 16},
3,
"LOG",
true};
@@ -93,19 +89,21 @@ class ACARSAppView : public View {
Console console{
{0, 3 * 16, 240, 256}};
AudioVolumeField field_volume{
{28 * 8, 1 * 16}};
std::unique_ptr<ACARSLogger> logger{};
void on_packet(const acars::Packet& packet);
void on_packet(const ACARSPacketMessage* packet);
MessageHandlerRegistration message_handler_packet{
Message::ID::ACARSPacket,
[this](Message* const p) {
const auto message = static_cast<const ACARSPacketMessage*>(p);
const acars::Packet packet{message->packet};
this->on_packet(packet);
this->on_packet(message);
}};
};
} /* namespace ui */
} // namespace ui::external_app::acars_rx
#endif /*__ACARS_APP_H__*/
#endif /*__ACARS_APP_H__*/
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "acars_app.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::acars_rx {
void initialize_app(ui::NavigationView& nav) {
nav.push<ACARSAppView>();
}
} // namespace ui::external_app::acars_rx
extern "C" {
__attribute__((section(".external_app.app_acars_rx.application_information"), used)) application_information_t _application_information_acars_rx = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::acars_rx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "ACARS",
/*.bitmap_data = */ {
0x80,
0x01,
0xC0,
0x03,
0xC0,
0x03,
0xC0,
0x03,
0xC0,
0x03,
0xE0,
0x07,
0xF8,
0x1F,
0xFE,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xC0,
0x03,
0xC0,
0x03,
0xC0,
0x03,
0xE0,
0x07,
0xF0,
0x0F,
0xF8,
0x1F,
},
/*.icon_color = */ ui::Color::orange().v,
/*.menu_location = */ app_location_t::RX,
/*.m4_app_tag = portapack::spi_flash::image_tag_acars */ {'P', 'A', 'C', 'A'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+1 -1
View File
@@ -76,7 +76,7 @@ __attribute__((section(".external_app.app_blespam.application_information"), use
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'B', 'T', 'T'},
/*.m4_app_tag = portapack::spi_flash::image_tag_btle_tx */ {'P', 'B', 'T', 'T'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+23
View File
@@ -88,6 +88,25 @@ set(EXTCPPSRC
#adsbtx
external/adsbtx/main.cpp
external/adsbtx/ui_adsb_tx.cpp
#morse_tx
external/morse_tx/main.cpp
external/morse_tx/ui_morse.cpp
#sstvtx
external/sstvtx/main.cpp
external/sstvtx/ui_sstvtx.cpp
#random
external/random_password/main.cpp
external/random_password/ui_random_password.cpp
external/random_password/sha512.cpp
external/random_password/sha512.h
#acars
external/acars_rx/main.cpp
external/acars_rx/acars_app.cpp
)
set(EXTAPPLIST
@@ -112,4 +131,8 @@ set(EXTAPPLIST
tpmsrx
protoview
adsbtx
morse_tx
sstvtx
random_password
#acars_rx
)
+31
View File
@@ -44,6 +44,10 @@ MEMORY
ram_external_app_tpmsrx(rwx) : org = 0xADC30000, len = 32k
ram_external_app_protoview(rwx) : org = 0xADC40000, len = 32k
ram_external_app_adsbtx(rwx) : org = 0xADC50000, len = 32k
ram_external_app_morse_tx(rwx) : org = 0xADC60000, len = 32k
ram_external_app_sstvtx(rwx) : org = 0xADC70000, len = 32k
ram_external_app_random_password(rwx) : org = 0xADC80000, len = 32k
ram_external_app_acars_rx(rwx) : org = 0xADC90000, len = 32k
}
SECTIONS
@@ -176,4 +180,31 @@ SECTIONS
} > ram_external_app_adsbtx
.external_app_morse_tx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_morse_tx.application_information));
*(*ui*external_app*morse_tx*);
} > ram_external_app_morse_tx
.external_app_sstvtx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_sstvtx.application_information));
*(*ui*external_app*sstvtx*);
} > ram_external_app_sstvtx
.external_app_random_password : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_random_password.application_information));
*(*ui*external_app*random_password*);
} > ram_external_app_random_password
.external_app_acars_rx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_acars_rx.application_information));
*(*ui*external_app*acars_rx*);
} > ram_external_app_acars_rx
}
+33 -4
View File
@@ -22,6 +22,7 @@
#include "rtc_time.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "i2cdevmanager.hpp"
using namespace portapack;
using namespace ui;
@@ -29,7 +30,7 @@ using namespace ui;
namespace ui::external_app::extsensors {
void ExtSensorsView::focus() {
text_info.focus();
console.focus();
}
ExtSensorsView::ExtSensorsView(NavigationView& nav)
@@ -39,10 +40,33 @@ ExtSensorsView::ExtSensorsView(NavigationView& nav)
&text_gps,
&text_orientation,
&text_envl1,
&text_envl2});
&text_envl2,
&text_envl3,
&console});
prev_scan_int = i2cdev::I2CDevManager::get_autoscan_interval();
refreshi2c();
i2cdev::I2CDevManager::set_autoscan_interval(3); // scan each 3 sec for new i2c devices
}
void ExtSensorsView::on_new_dev() {
refreshi2c();
}
void ExtSensorsView::refreshi2c() {
console.clear(true);
console.writeln("Found I2C devices:");
auto addrlist = i2cdev::I2CDevManager::get_gev_list_by_addr();
for (size_t i = 0; i < addrlist.size(); ++i) {
console.write("0x");
console.write(to_string_hex(addrlist[i]));
console.write(", ");
}
if (addrlist.size() == 0) console.writeln("No I2C devs found.");
}
ExtSensorsView::~ExtSensorsView() {
i2cdev::I2CDevManager::set_autoscan_interval(prev_scan_int);
}
void ExtSensorsView::on_any() {
@@ -78,9 +102,14 @@ void ExtSensorsView::on_environment(const EnvironmentDataMessage* msg) {
tmp += "C";
tmp += "; H: " + to_string_decimal(msg->humidity, 1) + "%"; // humidity
text_envl1.set(tmp);
tmp = "P: " + to_string_decimal(msg->pressure, 1) + " hPa; L:"; // pressure
tmp += to_string_dec_int(msg->light) + " LUX"; // light
tmp = "P: " + to_string_decimal(msg->pressure, 1) + " hPa"; // pressure
text_envl2.set(tmp);
}
void ExtSensorsView::on_light(const LightDataMessage* msg) {
on_any();
std::string tmp = "L: " + to_string_dec_int(msg->light) + " LUX";
text_envl3.set(tmp);
}
} // namespace ui::external_app::extsensors
@@ -53,6 +53,7 @@ class ExtSensorsView : public View {
NavigationView& nav_;
bool has_data = false;
uint16_t prev_scan_int = 0;
Labels labels{
{{0 * 8, 3 * 16}, "GPS:", Theme::getInstance()->fg_light->foreground},
@@ -64,9 +65,15 @@ class ExtSensorsView : public View {
Text text_orientation{{5 * 8, 5 * 16, 24 * 8, 16}, "-"};
Text text_envl1{{5 * 8, 7 * 16, 24 * 8, 16}, "-"};
Text text_envl2{{1 * 8, 9 * 16, 24 * 8, 16}, "-"};
Text text_envl3{{1 * 8, 11 * 16, 24 * 8, 16}, "-"};
Console console{
{1, 13 * 16, screen_width - 1, screen_height - 13 * 16}};
void refreshi2c();
void on_new_dev();
void on_any();
void on_light(const LightDataMessage* msg);
void on_gps(const GPSPosDataMessage* msg);
void on_orientation(const OrientationDataMessage* msg);
void on_environment(const EnvironmentDataMessage* msg);
@@ -90,6 +97,20 @@ class ExtSensorsView : public View {
const auto message = static_cast<const EnvironmentDataMessage*>(p);
this->on_environment(message);
}};
MessageHandlerRegistration message_handler_light{
Message::ID::LightData,
[this](Message* const p) {
const auto message = static_cast<const LightDataMessage*>(p);
this->on_light(message);
}};
MessageHandlerRegistration message_handler_dev{
Message::ID::I2CDevListChanged,
[this](Message* const p) {
(void)p; // make compiler happy
this->on_new_dev();
}};
};
}; // namespace ui::external_app::extsensors
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_morse.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::morse_tx {
void initialize_app(ui::NavigationView& nav) {
nav.push<MorseView>();
}
} // namespace ui::external_app::morse_tx
extern "C" {
__attribute__((section(".external_app.app_morse_tx.application_information"), used)) application_information_t _application_information_morse_tx = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::morse_tx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Morse",
/*.bitmap_data = */ {
0x00,
0x00,
0xFE,
0x7F,
0xFF,
0xFF,
0xBB,
0xD0,
0xFF,
0xFF,
0xFF,
0xFF,
0x0B,
0xE1,
0xFF,
0xFF,
0xFF,
0xFF,
0xEB,
0xD0,
0xFF,
0xFF,
0xFE,
0x7F,
0x70,
0x00,
0x30,
0x00,
0x10,
0x00,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_tones */ {'P', 'T', 'O', 'N'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -36,7 +36,7 @@ using namespace portapack;
using namespace morse;
using namespace hackrf::one;
namespace ui {
namespace ui::external_app::morse_tx {
static WORKING_AREA(ookthread_wa, 256);
@@ -194,7 +194,8 @@ void MorseView::set_foxhunt(size_t i) {
MorseView::MorseView(
NavigationView& nav)
: nav_(nav) {
baseband::run_image(portapack::spi_flash::image_tag_tones);
// baseband::run_image(portapack::spi_flash::image_tag_tones);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&labels,
&checkbox_foxhunt,
@@ -284,4 +285,4 @@ MorseView::MorseView(
};
}
} /* namespace ui */
} // namespace ui::external_app::morse_tx
@@ -39,7 +39,7 @@
using namespace morse;
namespace ui {
namespace ui::external_app::morse_tx {
class MorseView : public View {
public:
@@ -179,6 +179,6 @@ class MorseView : public View {
}};
};
} /* namespace ui */
} // namespace ui::external_app::morse_tx
#endif /*__MORSE_TX_H__*/
+50 -7
View File
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2024 HTotoo, zxkmm
*
* This file is part of PortaPack.
*
@@ -49,9 +48,12 @@ ProtoView::ProtoView(NavigationView& nav)
&field_vga,
&field_volume,
&field_frequency,
&labels,
&label_zoom,
&label_shift,
&options_zoom,
&number_shift,
&button_reset,
&button_pause,
&waveform,
&waveform2,
&waveform3,
@@ -63,9 +65,18 @@ ProtoView::ProtoView(NavigationView& nav)
draw();
draw2();
};
number_shift.on_change = [this](int32_t value) {
waveform_shift = value;
draw();
draw2();
};
button_reset.on_select = [this](Button&) {
reset();
};
button_pause.on_select = [this](Button&) {
set_pause(!paused);
};
set_pause(false); // need to use this to default hide shift functionality
baseband::set_subghzd_config(0, receiver_model.sampling_rate());
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
@@ -74,6 +85,9 @@ ProtoView::ProtoView(NavigationView& nav)
void ProtoView::reset() {
cnt = 0;
set_pause(false);
number_shift.set_value(0);
waveform_shift = 0;
for (uint16_t i = 0; i < MAXSIGNALBUFFER; i++) time_buffer[i] = 0;
needCntReset = false;
draw();
@@ -126,7 +140,18 @@ void ProtoView::draw() {
drawcnt = 0;
for (uint16_t i = 0; i < MAXDRAWCNT; i++) waveform_buffer[i] = 0; // reset
for (uint16_t i = 0; i < MAXSIGNALBUFFER; ++i) {
// add empty data for padding (so you can shift left/negative)
if (waveform_shift < 0) {
for (int32_t i = 0; (i < -1 * waveform_shift) && drawcnt < MAXDRAWCNT; // this is for shift negative (move to right)
++i) {
waveform_buffer[drawcnt++] = 0;
}
}
uint16_t skipped = 0;
uint16_t to_skip = ((waveform_shift > 0) ? waveform_shift : 0); // when >0 it'll skip some to move left
for (uint16_t i = 0;
i < MAXSIGNALBUFFER && drawcnt < MAXDRAWCNT; // prevent out of ranging
++i) {
state = time_buffer[i] >= 0;
int32_t timeabs = state ? time_buffer[i] : -1 * time_buffer[i];
int32_t timesize = timeabs / zoom;
@@ -145,9 +170,12 @@ void ProtoView::draw() {
}
remain = 0;
lmax = 0;
for (int32_t ii = 0; ii < timesize; ++ii) {
waveform_buffer[drawcnt++] = state;
if (drawcnt >= MAXDRAWCNT) return;
for (int32_t ii = 0; ii < timesize && drawcnt < MAXDRAWCNT; ++ii) {
if (skipped < to_skip) {
skipped++;
} else {
waveform_buffer[drawcnt++] = state;
}
}
}
}
@@ -158,6 +186,7 @@ void ProtoView::add_time(int32_t time) {
}
void ProtoView::on_data(const ProtoViewDataMessage* message) {
if (paused) return;
// filter out invalid ones.
uint16_t start = 0;
uint16_t stop = 0;
@@ -190,6 +219,20 @@ void ProtoView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
void ProtoView::set_pause(bool pause) {
paused = pause;
if (pause) {
label_shift.hidden(false);
number_shift.hidden(false);
button_pause.set_text(LanguageHelper::currentMessages[LANG_RESUME]);
} else {
label_shift.hidden(true);
number_shift.hidden(true);
button_pause.set_text(LanguageHelper::currentMessages[LANG_PAUSE]);
}
set_dirty();
}
ProtoView::~ProtoView() {
audio::output::stop();
receiver_model.disable();
+24 -7
View File
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
@@ -75,8 +74,12 @@ class ProtoView : public View {
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
nav_};
Labels labels{
// need to seperate because label shift need to hide independently
Labels label_zoom{
{{0 * 8, 1 * 16}, "Zoom: ", Theme::getInstance()->fg_light->foreground}};
Labels label_shift{
{{0 * 8, 2 * 16}, "Shift: ", Theme::getInstance()->fg_light->foreground}};
OptionsField options_zoom{
{7 * 8, 1 * 16},
@@ -92,12 +95,23 @@ class ProtoView : public View {
{"500", 500},
{"1000", 1000}}};
NumberField number_shift{
{7 * 8, 2 * 16},
5,
{-MAXSIGNALBUFFER, MAXSIGNALBUFFER},
1,
' '};
Button button_reset{
{screen_width - 12 * 8, 1 * 16, 96, 24},
LanguageHelper::currentMessages[LANG_RESET]};
Button button_pause{
{screen_width - 12 * 8, 1 * 16 + 24, 96, 24},
LanguageHelper::currentMessages[LANG_PAUSE]};
Waveform waveform{
{0, 5 * 8, 240, 50},
{0, 8 * 8, 240, 50},
waveform_buffer,
0,
0,
@@ -105,7 +119,7 @@ class ProtoView : public View {
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform2{
{0, 5 * 8 + 55, 240, 50},
{0, 8 * 8 + 55, 240, 50},
&waveform_buffer[MAXDRAWCNTPERWF],
0,
0,
@@ -113,7 +127,7 @@ class ProtoView : public View {
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform3{
{0, 5 * 8 + 110, 240, 50},
{0, 8 * 8 + 110, 240, 50},
&waveform_buffer[MAXDRAWCNTPERWF * 2],
0,
0,
@@ -121,7 +135,7 @@ class ProtoView : public View {
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform4{
{0, 5 * 8 + 165, 240, 50},
{0, 8 * 8 + 165, 240, 50},
&waveform_buffer[MAXDRAWCNTPERWF * 3],
0,
0,
@@ -129,8 +143,10 @@ class ProtoView : public View {
Theme::getInstance()->fg_yellow->foreground};
bool needCntReset = false;
bool paused = false;
int16_t zoom = 1; // one value in ms
int16_t waveform_shift = 0;
uint16_t cnt = 0; // pointer to next element
uint16_t drawcnt = 0; // pointer to draw next element
@@ -143,6 +159,7 @@ class ProtoView : public View {
void on_data(const ProtoViewDataMessage* message);
void draw();
void draw2();
void set_pause(bool pause);
void reset();
MessageHandlerRegistration message_handler_packet{
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_random_password.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::random_password {
void initialize_app(ui::NavigationView& nav) {
nav.push<RandomPasswordView>();
}
} // namespace ui::external_app::random_password
extern "C" {
__attribute__((section(".external_app.app_random_password.application_information"), used)) application_information_t _application_information_random_password = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::random_password::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Random passwd",
/*.bitmap_data = */ {
0xC0,
0x03,
0xE0,
0x07,
0x30,
0x0C,
0x30,
0x0C,
0x30,
0x0C,
0x30,
0x0C,
0xE0,
0x07,
0xC0,
0x03,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0x80,
0x01,
0x80,
0x07,
0x80,
0x03,
0x80,
0x07,
0x80,
0x01,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::UTILITIES,
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'A', 'F', 'R'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+156
View File
@@ -0,0 +1,156 @@
/* modified from https://github.com/ulwanski/sha512
* copyright @ulwanski
* */
#include "sha512.h"
namespace ui::external_app::random_password {
const unsigned long long SHA512::sha512_k[80] = // ULL = uint64
{0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL};
void SHA512::transform(const unsigned char* message, unsigned int block_nb) {
uint64 w[80];
uint64 wv[8];
uint64 t1, t2;
const unsigned char* sub_block;
int i, j;
for (i = 0; i < (int)block_nb; i++) {
sub_block = message + (i << 7);
for (j = 0; j < 16; j++) {
SHA2_PACK64(&sub_block[j << 3], &w[j]);
}
for (j = 16; j < 80; j++) {
w[j] = SHA512_F4(w[j - 2]) + w[j - 7] + SHA512_F3(w[j - 15]) + w[j - 16];
}
for (j = 0; j < 8; j++) {
wv[j] = m_h[j];
}
for (j = 0; j < 80; j++) {
t1 = wv[7] + SHA512_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6]) + sha512_k[j] + w[j];
t2 = SHA512_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]);
wv[7] = wv[6];
wv[6] = wv[5];
wv[5] = wv[4];
wv[4] = wv[3] + t1;
wv[3] = wv[2];
wv[2] = wv[1];
wv[1] = wv[0];
wv[0] = t1 + t2;
}
for (j = 0; j < 8; j++) {
m_h[j] += wv[j];
}
}
}
void SHA512::init() {
m_h[0] = 0x6a09e667f3bcc908ULL;
m_h[1] = 0xbb67ae8584caa73bULL;
m_h[2] = 0x3c6ef372fe94f82bULL;
m_h[3] = 0xa54ff53a5f1d36f1ULL;
m_h[4] = 0x510e527fade682d1ULL;
m_h[5] = 0x9b05688c2b3e6c1fULL;
m_h[6] = 0x1f83d9abfb41bd6bULL;
m_h[7] = 0x5be0cd19137e2179ULL;
m_len = 0;
m_tot_len = 0;
}
void SHA512::update(const unsigned char* message, unsigned int len) {
unsigned int block_nb;
unsigned int new_len, rem_len, tmp_len;
const unsigned char* shifted_message;
tmp_len = SHA384_512_BLOCK_SIZE - m_len;
rem_len = len < tmp_len ? len : tmp_len;
memcpy(&m_block[m_len], message, rem_len);
if (m_len + len < SHA384_512_BLOCK_SIZE) {
m_len += len;
return;
}
new_len = len - rem_len;
block_nb = new_len / SHA384_512_BLOCK_SIZE;
shifted_message = message + rem_len;
transform(m_block, 1);
transform(shifted_message, block_nb);
rem_len = new_len % SHA384_512_BLOCK_SIZE;
memcpy(m_block, &shifted_message[block_nb << 7], rem_len);
m_len = rem_len;
m_tot_len += (block_nb + 1) << 7;
}
void SHA512::final(unsigned char* digest) {
unsigned int block_nb;
unsigned int pm_len;
unsigned int len_b;
int i;
block_nb = 1 + ((SHA384_512_BLOCK_SIZE - 17) < (m_len % SHA384_512_BLOCK_SIZE));
len_b = (m_tot_len + m_len) << 3;
pm_len = block_nb << 7;
memset(m_block + m_len, 0, pm_len - m_len);
m_block[m_len] = 0x80;
SHA2_UNPACK32(len_b, m_block + pm_len - 4);
transform(m_block, block_nb);
for (i = 0; i < 8; i++) {
SHA2_UNPACK64(m_h[i], &digest[i << 3]);
}
}
std::string sha512(const void* dat, size_t len) {
unsigned char digest[SHA512::DIGEST_SIZE];
memset(digest, 0, SHA512::DIGEST_SIZE);
SHA512 ctx = SHA512();
ctx.init();
ctx.update((const unsigned char*)dat, len);
ctx.final(digest);
char buf[2 * SHA512::DIGEST_SIZE + 1];
buf[2 * SHA512::DIGEST_SIZE] = 0;
for (unsigned int i = 0; i < SHA512::DIGEST_SIZE; i++)
sprintf(buf + i * 2, "%02x", digest[i]);
return std::string(buf);
}
std::string sha512(std::string input) {
return sha512((const unsigned char*)input.c_str(), (size_t)input.length());
}
} // namespace ui::external_app::random_password
+76
View File
@@ -0,0 +1,76 @@
/* modified from https://github.com/ulwanski/sha512
* copyright @ulwanski
* */
#ifndef SHA512_H
#define SHA512_H
#include <string>
#include <cstring>
#include <fstream>
#define _CRT_SECURE_NO_WARNINGS
std::string sha512(const void* dat, size_t len);
std::string sha512(std::string input);
namespace ui::external_app::random_password {
class SHA512 {
protected:
typedef unsigned char uint8;
typedef unsigned long uint32;
typedef unsigned long long uint64;
const static uint64 sha512_k[];
static const unsigned int SHA384_512_BLOCK_SIZE = (1024 / 8);
public:
void init();
void update(const unsigned char* message, unsigned int len);
void final(unsigned char* digest);
static const unsigned int DIGEST_SIZE = (512 / 8);
protected:
void transform(const unsigned char* message, unsigned int block_nb);
unsigned int m_tot_len;
unsigned int m_len;
unsigned char m_block[2 * SHA384_512_BLOCK_SIZE];
uint64 m_h[8];
};
std::string sha512(std::string input);
#define SHA2_SHFR(x, n) (x >> n)
#define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
#define SHA2_ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
#define SHA2_CH(x, y, z) ((x & y) ^ (~x & z))
#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
#define SHA512_F1(x) (SHA2_ROTR(x, 28) ^ SHA2_ROTR(x, 34) ^ SHA2_ROTR(x, 39))
#define SHA512_F2(x) (SHA2_ROTR(x, 14) ^ SHA2_ROTR(x, 18) ^ SHA2_ROTR(x, 41))
#define SHA512_F3(x) (SHA2_ROTR(x, 1) ^ SHA2_ROTR(x, 8) ^ SHA2_SHFR(x, 7))
#define SHA512_F4(x) (SHA2_ROTR(x, 19) ^ SHA2_ROTR(x, 61) ^ SHA2_SHFR(x, 6))
#define SHA2_UNPACK32(x, str) \
{ \
*((str) + 3) = (uint8)((x)); \
*((str) + 2) = (uint8)((x) >> 8); \
*((str) + 1) = (uint8)((x) >> 16); \
*((str) + 0) = (uint8)((x) >> 24); \
}
#define SHA2_UNPACK64(x, str) \
{ \
*((str) + 7) = (uint8)((x)); \
*((str) + 6) = (uint8)((x) >> 8); \
*((str) + 5) = (uint8)((x) >> 16); \
*((str) + 4) = (uint8)((x) >> 24); \
*((str) + 3) = (uint8)((x) >> 32); \
*((str) + 2) = (uint8)((x) >> 40); \
*((str) + 1) = (uint8)((x) >> 48); \
*((str) + 0) = (uint8)((x) >> 56); \
}
#define SHA2_PACK64(str, x) \
{ \
*(x) = ((uint64) * ((str) + 7)) | ((uint64) * ((str) + 6) << 8) | ((uint64) * ((str) + 5) << 16) | ((uint64) * ((str) + 4) << 24) | ((uint64) * ((str) + 3) << 32) | ((uint64) * ((str) + 2) << 40) | ((uint64) * ((str) + 1) << 48) | ((uint64) * ((str) + 0) << 56); \
}
} // namespace ui::external_app::random_password
#endif
@@ -0,0 +1,397 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2024 zxkmm
* 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.
*/
/* Notes about safety/randomness:
* - Radio data is from nature, so if it's evenly tiled in space, it's random enough, but usually it's not
* - A unsafe point is the freq that fetch data is from LCG (from Cpp STL) using time as seed, but should be spread out by later random calculation
* - A unsafe point is the init data is still using LCG to generate, but used following methods to spread out:
* - - Radio data
* - - save a buffer of seeds and generate char by char, this cover all the possible chars combination
* - - Rollout with teo seeds and get a final char, this spread out again
* - - Hash out the generated password and spread out again if you choose. SHA-512 is proven very spreading out in random space by math
* - But still, this isn't meant to become a TRNG generator, don't use at critical/high safety requited/important system, this FOSS has absolutely no warranty
*/
#include "ui_random_password.hpp"
#include "ui_modemsetup.hpp"
#include "modems.hpp"
#include "rtc_time.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "file_path.hpp"
#include "sha512.h"
using namespace portapack;
using namespace modems;
using namespace ui;
namespace ui::external_app::random_password {
void RandomPasswordLogger::log_raw_data(const std::string& data) {
log_file.write_entry(data);
}
void RandomPasswordView::focus() {
button_refresh.focus();
}
RandomPasswordView::RandomPasswordView(NavigationView& nav)
: nav_{nav} {
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&rssi,
&channel,
&field_rf_amp,
&field_lna,
&field_vga,
&field_frequency,
&check_log,
&button_modem_setup,
&labels,
&text_generated_passwd,
&text_char_type_hints,
&check_digits,
&check_latin_lower,
&check_latin_upper,
&check_punctuation,
&check_show_seeds,
&check_auto_send,
&button_refresh,
&button_show_qr,
&button_flood,
&button_send,
&field_digits,
&field_method,
&check_allow_confusable_chars,
&text_seed,
&progressbar});
// no idea what's these, i copied from afsk rx app and they seems needed'
auto def_bell202 = &modem_defs[0];
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
serial_format_t serial_format;
serial_format.data_bits = 7;
serial_format.parity = EVEN;
serial_format.stop_bits = 1;
serial_format.bit_order = LSB_FIRST;
persistent_memory::set_serial_format(serial_format);
progressbar.set_max(MAX_DIGITS * 2);
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
if (v) {
nav_.display_modal(
"Warning",
"Sure?\n"
"this will save all generated\n"
"password to sdcard\n"
"in plain text\n"
"those which generated before\n"
"you check me, will lost",
YESNO,
[this, v](bool c) {
if (c) {
logging = v;
} else {
check_log.set_value(false);
// this is needed to check back to false cuz when trigger by human, the check to true already happened
// this blocked interface so won't accidently saved even if user checked but selected no later here,
// but take care of here if in the future implemented ticking/auto/batch save etc
}
});
} else {
logging = v;
}
};
button_modem_setup.on_select = [&nav](Button&) { // copied from afsk rx app
nav.push<ModemSetupView>();
};
check_digits.on_select = [this](Checkbox&, bool) {
this->new_password();
};
check_latin_lower.on_select = [this](Checkbox&, bool) {
this->new_password();
};
check_latin_upper.on_select = [this](Checkbox&, bool) {
this->new_password();
};
check_punctuation.on_select = [this](Checkbox&, bool) {
this->new_password();
};
check_allow_confusable_chars.on_select = [this](Checkbox&, bool) {
this->new_password();
};
button_refresh.on_select = [this](Button&) {
this->set_random_freq();
this->new_password();
};
button_show_qr.on_select = [this, &nav](Button&) {
nav.push<QRCodeView>(password.data());
};
button_flood.on_select = [this](Button&) {
if (flooding) {
flooding = false;
button_flood.set_text(LanguageHelper::currentMessages[LANG_FLOOD]);
} else {
flooding = true;
button_flood.set_text(LanguageHelper::currentMessages[LANG_STOP]);
}
};
button_send.on_select = [this, &nav](Button&) {
async_prev_val = portapack::async_tx_enabled;
portapack::async_tx_enabled = true;
UsbSerialAsyncmsg::asyncmsg(password);
portapack::async_tx_enabled = async_prev_val;
};
field_digits.on_change = [this](int32_t) {
clean_buffer();
this->new_password();
};
/// v check defauly val init
check_digits.set_value(true);
check_latin_lower.set_value(true);
check_latin_upper.set_value(true);
check_punctuation.set_value(true);
check_show_seeds.set_value(true);
field_digits.set_value(16);
field_method.set_by_value(Method::RADIO_LCG_ROLL_HASH);
///^ check defauly val init
logger = std::make_unique<RandomPasswordLogger>();
if (logger)
logger->append(logs_dir / u"random.TXT");
// Auto-configure modem for LCR RX (will be removed later), copied from afsk rx app
baseband::set_afsk(persistent_memory::modem_baudrate(), 8, 0, false);
receiver_model.enable();
receiver_model.set_rf_amp(false);
set_random_freq();
new_password();
}
void RandomPasswordView::on_data(uint32_t value, bool is_data) {
if (is_data) {
seed = static_cast<unsigned int>(value);
text_seed.set(to_string_dec_uint(check_show_seeds.value() ? seed : 0));
/// v feed deque
seeds_deque.push_back(value);
if (seeds_deque.size() > MAX_DIGITS * 2) {
seeds_deque.pop_front();
}
///^ feed deque
progressbar.set_value(seeds_deque.size());
if (flooding && seeds_deque.size() >= MAX_DIGITS * 2) {
new_password();
}
} else {
text_generated_passwd.set("Baudrate estimation: ~");
text_char_type_hints.set(to_string_dec_uint(value));
}
}
void RandomPasswordView::clean_buffer() {
seeds_deque = {};
}
void RandomPasswordView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
void RandomPasswordView::set_random_freq() {
std::srand(LPC_RTC->CTIME0);
// this is only for seed to visit random freq, the radio is still real random
auto random_freq = 100000000 + (std::rand() % 900000000); // 100mhz to 1ghz
receiver_model.set_target_frequency(random_freq);
field_frequency.set_value(random_freq);
}
void RandomPasswordView::new_password() {
if (seeds_deque.size() < MAX_DIGITS * 2) {
seeds_buffer_not_full = true;
text_generated_passwd.set("wait seeds buffer full");
text_char_type_hints.set("then press generate");
return;
}
password = "";
std::string charset = "";
std::string char_type_hints = "";
std::string initial_password = "";
int password_length = field_digits.value();
/// charset worker
if (check_digits.value())
charset += "0123456789";
if (check_latin_lower.value())
charset += "abcdefghijklmnopqrstuvwxyz";
if (check_latin_upper.value())
charset += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (check_punctuation.value())
charset += ".,-!?";
if (!check_allow_confusable_chars.value()) {
charset.erase(std::remove_if(charset.begin(), charset.end(),
[](char c) { return c == '0' || c == 'O' || c == 'o' || c == '1' || c == 'l'; }),
charset.end());
}
if (charset.empty()) {
text_generated_passwd.set("generate failed,");
text_char_type_hints.set("select at least 1 type");
return;
}
/// roll worker
for (int i = 0; i < password_length * 2; i += 2) {
unsigned int seed = seeds_deque[i];
std::srand(seed);
uint8_t rollnum = (uint8_t)(seeds_deque[i + 1] % 128);
uint8_t nu = 0;
for (uint8_t o = 0; o < rollnum; ++o) nu = std::rand();
nu++;
char c = charset[std::rand() % charset.length()];
initial_password += c;
}
// hash worker
std::string hashed_password = sha512(initial_password);
std::vector<char> password_chars(password_length);
for (int i = 0; i < password_length; i++) {
unsigned int index = std::stoul(hashed_password.substr(i * 2, 2), nullptr, 16) % charset.length(); // result from 0 to charset.length()-1,should be very evenly tiled in the charset space
password_chars[i] = charset[index];
}
/// hint text worker
for (char c : password_chars) {
password += c;
if (std::isdigit(c)) {
char_type_hints += "1";
} else if (std::islower(c)) {
char_type_hints += "a";
} else if (std::isupper(c)) {
char_type_hints += "A";
} else {
char_type_hints += ",";
}
}
/// decision worker
switch (field_method.selected_index_value()) {
case Method::RADIO_LCG_ROLL:
password = initial_password;
break;
case Method::RADIO_LCG_ROLL_HASH:
break;
default:
break;
}
/// give out result worker
text_generated_passwd.set(password);
text_char_type_hints.set(char_type_hints);
paint_password_hints();
if (logger && logging) {
str_log += generate_log_line();
logger->log_raw_data(str_log);
str_log = "";
}
if (check_auto_send.value() || flooding) {
async_prev_val = portapack::async_tx_enabled;
portapack::async_tx_enabled = true;
UsbSerialAsyncmsg::asyncmsg(password);
portapack::async_tx_enabled = async_prev_val;
}
clean_buffer();
}
void RandomPasswordView::paint_password_hints() {
Painter painter;
const int char_width = 8;
const int char_height = 16;
const int start_y = 6 * char_height;
const int rect_height = 4;
for (size_t i = 0; i < password.length(); i++) {
char c = password[i];
Color color;
if (std::isdigit(c)) {
color = Color::red();
} else if (std::islower(c)) {
color = Color::green();
} else if (std::isupper(c)) {
color = Color::blue();
} else {
color = Color::white();
}
painter.fill_rectangle(
{{static_cast<int>(i) * char_width, start_y},
{char_width, rect_height}},
color);
}
}
std::string RandomPasswordView::generate_log_line() {
std::string seeds_set = "";
for (auto seed : seeds_deque) {
seeds_set += std::to_string(seed);
seeds_set += " ";
}
std::string line = "\npassword=" + password +
"\nseeds=" + seeds_set +
"\n";
return line;
}
RandomPasswordView::~RandomPasswordView() {
receiver_model.disable();
baseband::shutdown();
}
} // namespace ui::external_app::random_password
@@ -0,0 +1,235 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2024 zxkmm
* 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_RANDOM_PASSWORD_H__
#define __UI_RANDOM_PASSWORD_H__
#define MAX_DIGITS 30
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_record_view.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "log_file.hpp"
#include "utility.hpp"
#include "ui_qrcode.hpp"
#include "usb_serial_asyncmsg.hpp"
#include "sha512.h"
#include <deque>
#include <string>
using namespace ui;
namespace ui::external_app::random_password {
enum Method {
RADIO_LCG_ROLL,
RADIO_LCG_ROLL_HASH,
};
class RandomPasswordLogger {
public:
Optional<File::Error> append(const std::filesystem::path& filename) {
return log_file.append(filename);
}
void log_raw_data(const std::string& data);
private:
LogFile log_file{};
};
class RandomPasswordView : public View {
public:
RandomPasswordView(NavigationView& nav);
~RandomPasswordView();
void focus() override;
std::string title() const override { return "R.passwd"; };
private:
unsigned int seed = 0; // extern void srand (unsigned int __seed) __THROW;
std::string password = "";
std::deque<unsigned int> seeds_deque = {0};
bool seeds_buffer_not_full = true;
bool in_benchmark = false;
bool flooding = false;
bool logging = false;
bool async_prev_val = false;
std::string str_log{""};
void on_data(uint32_t value, bool is_data);
void clean_buffer();
void new_password();
std::string generate_log_line();
void paint_password_hints();
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_passgen", app_settings::Mode::RX};
Labels labels{
{{0 * 8, 0 * 16}, "------------seeds-------------", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 3 * 16}, "-----------password-----------", Theme::getInstance()->fg_light->foreground},
{{0 * 8, 7 * 16 - 2}, "digits:", Theme::getInstance()->fg_light->foreground},
{{screen_width / 2, 7 * 16 - 2}, "method:", Theme::getInstance()->fg_light->foreground},
};
RFAmpField field_rf_amp{
{13 * 8, 1 * 16}};
LNAGainField field_lna{
{15 * 8, 1 * 16}};
VGAGainField field_vga{
{18 * 8, 1 * 16}};
RSSI rssi{
{21 * 8, 1 * 16 + 0, 6 * 8, 4}};
Channel channel{
{21 * 8, 1 * 16 + 5, 6 * 8, 4}};
RxFrequencyField field_frequency{
{0 * 8, 1 * 16},
nav_};
Button button_modem_setup{
{screen_width - 12 * 8, 2 * 16 - 1, 96, 16 + 2},
LanguageHelper::currentMessages[LANG_MODEM_SETUP]};
Text text_seed{
{0, 2 * 16, 10 * 8, 16},
"0000000000"};
ProgressBar progressbar{
{10 * 8 + 2, 2 * 16, screen_width - 96 - (10 * 8 + 4) - 1, 16}};
Text text_generated_passwd{
{0, 4 * 16, screen_width, 16},
"000000000000000000000000000000"};
Text text_char_type_hints{
{0, 5 * 16 + 4, screen_width, 16},
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"};
Checkbox check_show_seeds{
{17 * 8, 8 * 16},
6,
"Show seed"};
Checkbox check_auto_send{
{1 * 8, 8 * 16},
20,
"Auto send"};
Checkbox check_punctuation{
{17 * 8, 12 * 16},
6,
".,-!?"};
Checkbox check_allow_confusable_chars{
{1 * 8, 10 * 16},
20,
"0 O o 1 l"};
Checkbox check_digits{
{1 * 8, 12 * 16},
3,
"123"};
Checkbox check_latin_lower{
{1 * 8, 14 * 16},
3,
"abc"};
Checkbox check_latin_upper{
{17 * 8, 14 * 16},
3,
"ABC"};
Checkbox check_log{
{17 * 8, 10 * 16},
3,
LanguageHelper::currentMessages[LANG_SAVE]};
Button button_flood{
{0 * 8, 15 * 16 + 18, screen_width / 2, 22},
LanguageHelper::currentMessages[LANG_FLOOD]};
Button button_send{
{screen_width / 2 + 2, 15 * 16 + 18, screen_width / 2 - 2, 22},
"Send pwd"};
Button button_refresh{
{0 * 8, 17 * 16 + 10, screen_width / 2, 22},
"Generate"};
Button button_show_qr{
{screen_width / 2 + 2, 17 * 16 + 10, screen_width / 2 - 2, 22},
LanguageHelper::currentMessages[LANG_SHOWQR]};
NumberField field_digits{
{0 + (sizeof("digits:") - 1) * 8, 7 * 16 - 2},
2,
{1, 30},
1,
' '};
OptionsField field_method{
{(screen_width / 2) + (sizeof("method:") - 1) * 8, 7 * 16 - 2},
sizeof("R+L+R+H"),
{{"R+L+R", Method::RADIO_LCG_ROLL},
{"R+L+R+H", Method::RADIO_LCG_ROLL_HASH}}};
void on_data_afsk(const AFSKDataMessage& message);
std::unique_ptr<RandomPasswordLogger> logger{};
MessageHandlerRegistration message_handler_packet{
Message::ID::AFSKData,
[this](Message* const p) {
const auto message = static_cast<const AFSKDataMessage*>(p);
this->on_data(message->value, message->is_data);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
void set_random_freq();
};
} // namespace ui::external_app::random_password
#endif /*__UI_RANDOM_PASSWORD_H__*/
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_sstvtx.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::sstvtx {
void initialize_app(ui::NavigationView& nav) {
nav.push<SSTVTXView>();
}
} // namespace ui::external_app::sstvtx
extern "C" {
__attribute__((section(".external_app.app_sstvtx.application_information"), used)) application_information_t _application_information_sstvtx = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::sstvtx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "SSTV",
/*.bitmap_data = */ {
0x00,
0x00,
0x00,
0x00,
0xFE,
0x7F,
0x03,
0xC0,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0x03,
0xC0,
0xFF,
0xFF,
0xFB,
0xD7,
0xFE,
0x7F,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_sstvtx */ {'P', 'S', 'T', 'X'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -31,7 +31,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::sstvtx {
void SSTVTXView::focus() {
if (file_error)
@@ -232,7 +232,8 @@ SSTVTXView::SSTVTXView(
// Maybe this could be merged with proc_tones ? Pretty much the same except lots
// of different tones (256+)
baseband::run_image(portapack::spi_flash::image_tag_sstv_tx);
// baseband::run_image(portapack::spi_flash::image_tag_sstv_tx);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&labels,
&options_bitmaps,
@@ -283,4 +284,4 @@ SSTVTXView::SSTVTXView(
};
}
} /* namespace ui */
} // namespace ui::external_app::sstvtx
@@ -38,7 +38,7 @@
using namespace sstv;
namespace ui {
namespace ui::external_app::sstvtx {
class SSTVTXView : public View {
public:
@@ -110,6 +110,6 @@ class SSTVTXView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::sstvtx */
#endif /*__UI_SSTVTX_H__*/
+7 -6
View File
@@ -107,7 +107,7 @@ class TPMSAppView : public View {
private:
RxRadioState radio_state_{
314950000 /* frequency*/
314900000 /* frequency*/
,
1750000 /* bandwidth */,
2457600 /* sampling rate */
@@ -147,20 +147,21 @@ class TPMSAppView : public View {
// "315 MHz" TPMS sensors transmit at either 314.9 or 315 MHz but we should pick up either
OptionsField options_band{
{0 * 8, 0 * 16},
3,
5,
{
{"315", 314950000},
{"434", 433920000},
{"314.9", 314900000},
{"315.0", 315000000},
{"433.9", 433920000},
}};
OptionsField options_pressure{
{5 * 8, 0 * 16},
{6 * 8, 0 * 16},
3,
{{"kPa", 0},
{"PSI", 1}}};
OptionsField options_temperature{
{9 * 8, 0 * 16},
{10 * 8, 0 * 16},
2,
{{STR_DEGREES_C, 0},
{STR_DEGREES_F, 1}}};
+80 -18
View File
@@ -23,6 +23,7 @@
#include "string_format.hpp"
#include "file_path.hpp"
#include "metadata_file.hpp"
#include "flipper_subfile.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
@@ -36,14 +37,14 @@ void WardriveMapView::focus() {
// needs to load on every map change, because won't store or draw all while marker is not in the current view.
// todo optimize this somehow
bool WardriveMapView::load_markers() {
uint16_t cnt = 0;
void WardriveMapView::load_markers() {
uint16_t displayed_cnt = 0;
uint16_t cnt = 0;
geomap.clear_markers();
// serach for files with geotag, and add it to geomap as marker with tag. limit to N bc of mem limit.
for (const auto& entry : std::filesystem::directory_iterator(captures_dir, u"*.txt")) {
if (std::filesystem::is_regular_file(entry.status())) {
if (displayed_cnt > 30) break;
if (markers_counted && displayed_cnt > ui::GeoMap::NumMarkerListElements) return; // only if not fist iteration, since then not counted all elements
std::filesystem::path pth = captures_dir;
pth += u"/" + entry.path();
auto metadata_path = get_metadata_path(pth);
@@ -51,23 +52,65 @@ bool WardriveMapView::load_markers() {
if (metadata) {
if (metadata.value().latitude != 0 && metadata.value().longitude != 0 && metadata.value().latitude < 400 && metadata.value().longitude < 400) {
if (first_init == false) {
// move map there before add, so will display this.
geopos.set_report_change(false);
geopos.set_lat(metadata.value().latitude);
geopos.set_lon(metadata.value().longitude);
geopos.set_report_change(true);
geomap.move(metadata.value().longitude, metadata.value().latitude);
first_init = true;
// skip nth
if (marker_start <= cnt) {
if (first_init == false) {
// move map there before add, so will display this.
geopos.set_report_change(false);
geopos.set_lat(metadata.value().latitude);
geopos.set_lon(metadata.value().longitude);
geopos.set_report_change(true);
geomap.move(metadata.value().longitude, metadata.value().latitude);
first_init = true;
}
GeoMarker tmp{metadata.value().latitude, metadata.value().longitude, 400, entry.path().filename().string()};
if (geomap.store_marker(tmp) == MapMarkerStored::MARKER_STORED) displayed_cnt++;
if (!markers_counted) marker_cntall++;
}
GeoMarker tmp{metadata.value().latitude, metadata.value().longitude, 400, entry.path().filename().string()};
if (geomap.store_marker(tmp) == MapMarkerStored::MARKER_STORED) displayed_cnt++;
cnt++;
}
}
}
}
return (cnt > 0);
// load flipper files too
for (const auto& entry : std::filesystem::directory_iterator(flippersub_dir, u"*.sub")) {
if (std::filesystem::is_regular_file(entry.status())) {
if (markers_counted && displayed_cnt > ui::GeoMap::NumMarkerListElements) return; // only if not fist iteration, since then not counted all elements
std::filesystem::path pth = flippersub_dir;
pth += u"/" + entry.path();
auto metadata = read_flippersub_file(pth);
if (metadata) {
if (metadata.value().latitude != 0 && metadata.value().longitude != 0 && metadata.value().latitude < 400 && metadata.value().longitude < 400) {
// skip nth
if (marker_start <= cnt) {
if (first_init == false) {
// move map there before add, so will display this.
geopos.set_report_change(false);
geopos.set_lat(metadata.value().latitude);
geopos.set_lon(metadata.value().longitude);
geopos.set_report_change(true);
geomap.move(metadata.value().longitude, metadata.value().latitude);
first_init = true;
}
GeoMarker tmp{metadata.value().latitude, metadata.value().longitude, 400, entry.path().filename().string()};
if (geomap.store_marker(tmp) == MapMarkerStored::MARKER_STORED) displayed_cnt++;
if (!markers_counted) marker_cntall++;
}
cnt++;
}
}
}
}
markers_counted = true;
// show / hide paginator buttons
btn_back.hidden((marker_start == 0) || (marker_cntall == 0));
btn_next.hidden(((marker_start + ui::GeoMap::NumMarkerListElements) >= marker_cntall) || (marker_cntall == 0));
// update text
text_info.set(to_string_dec_uint(marker_start + 1) + " - " + to_string_dec_uint(displayed_cnt + marker_start) + " / " + to_string_dec_uint(marker_cntall));
set_dirty();
return;
}
WardriveMapView::WardriveMapView(NavigationView& nav)
@@ -75,7 +118,9 @@ WardriveMapView::WardriveMapView(NavigationView& nav)
add_children({&text_info,
&geomap,
&geopos,
&text_notfound});
&text_notfound,
&btn_back,
&btn_next});
geomap.set_mode(DISPLAY);
geomap.set_manual_panning(false);
@@ -100,21 +145,38 @@ WardriveMapView::WardriveMapView(NavigationView& nav)
load_markers();
geomap.set_dirty();
};
text_notfound.hidden(true);
geomap.init();
// load markers
if (load_markers()) {
load_markers();
if (marker_cntall > 0) {
text_notfound.hidden(true);
geomap.set_dirty();
} else {
geomap.hidden(true);
geopos.hidden(true);
text_notfound.hidden(false);
text_info.hidden(true);
}
// never move this before the first load() bc that will mess load up
geomap.on_move = [this](float lon, float lat) {
(void)lon;
(void)lat;
load_markers();
};
btn_back.on_select = [this, &nav](Button&) {
if (marker_start - ui::GeoMap::NumMarkerListElements >= 0)
marker_start = marker_start - ui::GeoMap::NumMarkerListElements;
else
marker_start = 0;
load_markers();
};
btn_next.on_select = [this, &nav](Button&) {
if (marker_start + ui::GeoMap::NumMarkerListElements <= marker_cntall)
marker_start = marker_start + ui::GeoMap::NumMarkerListElements;
else
marker_start = marker_cntall - ui::GeoMap::NumMarkerListElements;
load_markers();
};
}
WardriveMapView::~WardriveMapView() {
+12 -10
View File
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2024 HTotoo
*
* This file is part of PortaPack.
*
@@ -20,10 +19,6 @@
* Boston, MA 02110-1301, USA.
*/
// Code from https://github.com/Flipper-XFW/Xtreme-Apps/tree/04c3a60093e2c2378e79498b4505aa8072980a42/ble_spam/protocols
// Thanks for the work of the original creators!
// Saying thanks in the main view!
#ifndef __UI_WARDRIVEMAP_H__
#define __UI_WARDRIVEMAP_H__
@@ -50,22 +45,29 @@ class WardriveMapView : public View {
};
private:
const std::filesystem::path flippersub_dir = u"subghz";
NavigationView& nav_;
Text text_info{{0 * 8, 0 * 8, 30 * 8, 16 * 1}, "All GEOTAG from CAPTURES"};
Text text_notfound{{0 * 8, 3 * 8, 30 * 8, 16 * 1}, "No GeoTagged captures found"};
Text text_info{{0 * 8, 0 * 8, 20 * 8, 16 * 1}, "0 / 30"};
Text text_notfound{{0 * 8, 0 * 8, 30 * 8, 16 * 1}, "No GeoTagged captures found"};
GeoPos geopos{
{0, 20},
GeoPos::alt_unit::METERS,
GeoPos::spd_unit::HIDDEN};
GeoMap geomap{{0, 75, 240, 320 - 75}};
Button btn_back{{22 * 8, 0 * 8, 3 * 8, 16}, "<-"};
Button btn_next{{26 * 8, 0 * 8, 3 * 8, 16}, "->"};
void on_gps(const GPSPosDataMessage* msg);
void on_orientation(const OrientationDataMessage* msg);
bool load_markers(); // returns true if any exists, false if none.
void load_markers(); // returns true if any exists, false if none.
bool first_init = false;
bool first_init = false; // to center map to first marker, before callback is set
bool markers_counted = false; // to iterate all files on first, but only on first.
uint16_t marker_start = 0; // for paginator, this will be the first displayed
uint16_t marker_cntall = 0; // all geotagged marker count
MessageHandlerRegistration message_handler_gps{
Message::ID::GPSPosData,
+123
View File
@@ -0,0 +1,123 @@
/*
* 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 "flipper_subfile.hpp"
#include "convert.hpp"
#include "file_reader.hpp"
#include "string_format.hpp"
#include <string_view>
namespace fs = std::filesystem;
using namespace std::literals;
const std::string_view filetype_name = "Filetype"sv;
const std::string_view frequency_name = "Frequency"sv;
const std::string_view latitude_name_old = "Latitute"sv;
const std::string_view longitude_name_old = "Longitude"sv;
const std::string_view latitude_name = "Lat"sv;
const std::string_view longitude_name = "Lon"sv;
const std::string_view protocol_name = "Protocol"sv;
const std::string_view preset_name = "Preset"sv;
const std::string_view te_name = "TE"sv; // only in BinRAW
/*
Filetype: Flipper SubGhz Key File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Latitute: nan
Longitude: nan
Protocol: BinRAW
Bit: 1730
TE: 495
Bit_RAW: 1730
Data_RAW: 02 10 84
te: is the quantization interval, in us.
bit: all bit counts in file.
bit_raw: the bits stored in the next data_raw. this 2 can repeat
data_raw: is an encoded sequence of durations, where each bit in the sequence encodes one TE interval: 1 - high level (there is a carrier), 0 - low (no carrier). For example, TE=100, Bit_RAW=8, Data_RAW=0x37 => 0b00110111, that is, -200 200 -100 300 will be transmitted
Filetype: Flipper SubGhz RAW File
Version: 1
Frequency: 433920000
Preset: FuriHalSubGhzPresetOok650Async
Protocol: RAW
RAW_Data: 5832 -12188 130 -162
raw_data- positive: carrier for n time, negative: no carrier for n time. (us)
*/
Optional<flippersub_metadata> read_flippersub_file(const fs::path& path) {
File f;
auto error = f.open(path);
if (error)
return {};
flippersub_metadata metadata{};
auto reader = FileLineReader(f);
for (const auto& line : reader) {
auto cols = split_string(line, ':');
if (cols.size() != 2)
continue; // Bad line.
if (cols[1].length() <= 1) continue;
std::string fixed = cols[1].data() + 1;
fixed = trim(fixed);
if (cols[0] == filetype_name) {
if (fixed != "Flipper SubGhz Key File" && fixed != "Flipper SubGhz RAW File") return {}; // not supported
} else if (cols[0] == frequency_name)
parse_int(fixed, metadata.center_frequency);
else if (cols[0] == latitude_name)
parse_float_meta(fixed, metadata.latitude);
else if (cols[0] == longitude_name)
parse_float_meta(fixed, metadata.longitude);
else if (cols[0] == latitude_name_old)
parse_float_meta(fixed, metadata.latitude);
else if (cols[0] == longitude_name_old)
parse_float_meta(fixed, metadata.longitude);
else if (cols[0] == protocol_name) {
if (fixed == "RAW") metadata.protocol = FLIPPER_PROTO_RAW;
if (fixed == "BinRAW") metadata.protocol = FLIPPER_PROTO_BINRAW;
} else if (cols[0] == te_name) {
metadata.te = atoi(fixed.c_str());
} else if (cols[0] == preset_name) {
if (fixed.find("FSK") != std::string::npos) {
metadata.preset = FLIPPER_PRESET_2FSK;
} else if (fixed.find("Ook") != std::string::npos) {
metadata.preset = FLIPPER_PRESET_OOK;
} else if (fixed.find("Custom") != std::string::npos) {
metadata.preset = FLIPPER_PRESET_CUSTOM;
}
} else
continue;
}
if (metadata.center_frequency == 0) return {}; // Parse failed.
return metadata;
}
+53
View File
@@ -0,0 +1,53 @@
/*
* 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 __FLIPPER_SUBFILE_HPP__
#define __FLIPPER_SUBFILE_HPP__
#include "metadata_file.hpp"
typedef enum : uint8_t {
FLIPPER_PROTO_UNSUPPORTED = 0,
FLIPPER_PROTO_RAW = 1,
FLIPPER_PROTO_BINRAW = 2
} FlipperProto;
typedef enum : uint8_t {
FLIPPER_PRESET_UNK = 0,
FLIPPER_PRESET_CUSTOM = 1,
FLIPPER_PRESET_OOK = 2,
FLIPPER_PRESET_2FSK = 3,
} FlipperPreset;
struct flippersub_metadata {
rf::Frequency center_frequency;
float latitude = 0;
float longitude = 0;
FlipperProto protocol = FLIPPER_PROTO_UNSUPPORTED;
FlipperPreset preset = FLIPPER_PRESET_UNK;
uint16_t te = 0;
};
Optional<flippersub_metadata> read_flippersub_file(const std::filesystem::path& path);
// Maybe sometime there will be a data part reader / converter
#endif // __FLIPPER_SUBFILE_HPP__
+4 -1
View File
@@ -53,6 +53,8 @@ using asahi_kasei::ak4951::AK4951;
#include "string_format.hpp"
#include "bitmap.hpp"
#include "ui_widget.hpp"
#include "i2cdevmanager.hpp"
#include "battery.hpp"
namespace portapack {
@@ -588,7 +590,8 @@ init_status_t init() {
chThdSleepMilliseconds(10);
audio::init(portapack_audio_codec());
battery::BatteryManagement::init(persistent_memory::ui_override_batt_calc());
battery::BatteryManagement::set_calc_override(persistent_memory::ui_override_batt_calc());
i2cdev::I2CDevManager::init();
if (lcd_fast_setup)
draw_splash_screen_icon(4, ui::bitmap_icon_speaker);
-4
View File
@@ -33,13 +33,9 @@
#include "backlight.hpp"
#include "usb_serial.hpp"
#include "ads1110.hpp"
#include "max17055.hpp"
#include "radio.hpp"
#include "clock_manager.hpp"
#include "temperature_logger.hpp"
#include "battery.hpp"
#include "theme.hpp"
/* TODO: This would be better as a class to add
+1 -1
View File
@@ -52,7 +52,7 @@ void Tab::paint(Painter& painter) {
painter.fill_rectangle({rect.left(), rect.top(), rect.width() - 8, rect.height()}, color);
if (!highlighted())
painter.draw_hline({rect.left(), rect.top()}, rect.width() - 9, Theme::getInstance()->fg_light->foreground);
painter.draw_hline({rect.left(), rect.top()}, rect.width() - 9, Theme::getInstance()->bg_light->background);
painter.draw_bitmap(
{rect.right() - 8, rect.top()},
+73 -32
View File
@@ -55,7 +55,7 @@
#include "ui_level.hpp"
#include "ui_looking_glass_app.hpp"
#include "ui_mictx.hpp"
#include "ui_morse.hpp"
// #include "ui_morse.hpp" //moved to ext
// #include "ui_nrf_rx.hpp" //moved to ext
// #include "ui_numbers.hpp"
// #include "ui_nuoptix.hpp"
@@ -74,7 +74,7 @@
#include "ui_sonde.hpp"
// #include "ui_spectrum_painter.hpp" //moved to ext app
#include "ui_ss_viewer.hpp"
#include "ui_sstvtx.hpp"
// #include "ui_sstvtx.hpp" //moved to ext
// #include "ui_test.hpp"
#include "ui_text_editor.hpp"
#include "ui_tone_search.hpp"
@@ -86,11 +86,10 @@
#include "ui_battinfo.hpp"
#include "ui_external_items_menu_loader.hpp"
// #include "acars_app.hpp"
#include "ais_app.hpp"
#include "analog_audio_app.hpp"
// #include "analog_tv_app.hpp" //moved to ext
#include "ble_comm_app.hpp"
// #include "ble_comm_app.hpp"
#include "ble_rx_app.hpp"
#include "ble_tx_app.hpp"
#include "capture_app.hpp"
@@ -157,7 +156,6 @@ const NavigationView::AppList NavigationView::appList = {
{nullptr, "Debug", HOME, Color::light_grey(), &bitmap_icon_debug, new ViewFactory<DebugMenuView>()},
//{"about", "About", HOME, Color::cyan(), nullptr, new ViewFactory<AboutView>()},
/* RX ********************************************************************/
//{"acars", "ACARS", RX, Color::yellow(), &bitmap_icon_adsb, new ViewFactory<ACARSAppView>()},
{"adsbrx", "ADS-B", RX, Color::green(), &bitmap_icon_adsb, new ViewFactory<ADSBRxView>()},
{"ais", "AIS Boats", RX, Color::green(), &bitmap_icon_ais, new ViewFactory<AISAppView>()},
{"aprsrx", "APRS", RX, Color::green(), &bitmap_icon_aprs, new ViewFactory<APRSRXView>()},
@@ -185,13 +183,13 @@ const NavigationView::AppList NavigationView::appList = {
{"aprstx", "APRS TX", TX, ui::Color::green(), &bitmap_icon_aprs, new ViewFactory<APRSTXView>()},
{"bht", "BHT Xy/EP", TX, ui::Color::green(), &bitmap_icon_bht, new ViewFactory<BHTView>()},
{"bletx", "BLE Tx", TX, ui::Color::green(), &bitmap_icon_btle, new ViewFactory<BLETxView>()},
{"morse", "Morse", TX, ui::Color::green(), &bitmap_icon_morse, new ViewFactory<MorseView>()},
//{"morse", "Morse", TX, ui::Color::green(), &bitmap_icon_morse, new ViewFactory<MorseView>()}, //moved to ext
//{"nuoptixdtmf", "Nuoptix DTMF", TX, ui::Color::green(), &bitmap_icon_nuoptix, new ViewFactory<NuoptixView>()},
{"ooktx", "OOK", TX, ui::Color::yellow(), &bitmap_icon_remote, new ViewFactory<EncodersView>()},
{"pocsagtx", "POCSAG TX", TX, ui::Color::green(), &bitmap_icon_pocsag, new ViewFactory<POCSAGTXView>()},
{"rdstx", "RDS", TX, ui::Color::green(), &bitmap_icon_rds, new ViewFactory<RDSView>()},
{"soundbrd", "Soundbrd", TX, ui::Color::green(), &bitmap_icon_soundboard, new ViewFactory<SoundBoardView>()},
{"sstvtx", "SSTV", TX, ui::Color::green(), &bitmap_icon_sstv, new ViewFactory<SSTVTXView>()},
//{"sstvtx", "SSTV", TX, ui::Color::green(), &bitmap_icon_sstv, new ViewFactory<SSTVTXView>()}, //moved to ext
{"touchtune", "TouchTune", TX, ui::Color::green(), &bitmap_icon_touchtunes, new ViewFactory<TouchTunesView>()},
/* UTILITIES *************************************************************/
{"antennalength", "Antenna Length", UTILITIES, Color::green(), &bitmap_icon_tools_antenna, new ViewFactory<WhipCalcView>()},
@@ -388,7 +386,6 @@ void SystemStatusView::on_battery_data(const BatteryStateMessage* msg) {
if (!batt_was_inited) {
batt_was_inited = true;
refresh();
return;
}
if (!pmem::ui_hide_numeric_battery()) {
battery_text.set_battery(msg->valid_mask, msg->percent, msg->on_charger);
@@ -413,17 +410,14 @@ void SystemStatusView::refresh() {
// Display "Disable speaker" icon only if AK4951 Codec which has separate speaker/headphone control
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 (!pmem::ui_hide_fake_brightness() && !pmem::config_lcd_inverted_mode()) status_icons.add(&button_fake_brightness);
if (battery::BatteryManagement::isDetected()) {
batt_was_inited = true;
uint8_t percent = battery::BatteryManagement::getPercent();
if (!pmem::ui_hide_battery_icon()) {
status_icons.add(&battery_icon);
battery_text.set_battery(percent <= 100 ? 1 : 0, 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 <= 100 ? 1 : 0, percent, false);
}
}
@@ -450,7 +444,7 @@ void SystemStatusView::refresh() {
button_converter.set_foreground(pmem::config_converter() ? Theme::getInstance()->fg_red->foreground : Theme::getInstance()->fg_light->foreground);
// Fake Brightness
button_fake_brightness.set_foreground(pmem::apply_fake_brightness() ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
button_fake_brightness.set_foreground((pmem::apply_fake_brightness() & (!pmem::config_lcd_inverted_mode())) ? *Theme::getInstance()->status_active : Theme::getInstance()->fg_light->foreground);
set_dirty();
}
@@ -658,12 +652,10 @@ bool NavigationView::is_valid() const {
View* NavigationView::push_view(std::unique_ptr<View> new_view) {
free_view();
const auto p = new_view.get();
view_stack.emplace_back(ViewState{std::move(new_view), {}});
update_view();
return p;
}
@@ -722,8 +714,8 @@ void NavigationView::update_view() {
auto top_view = top.view.get();
add_child(top_view);
top_view->set_parent_rect({{0, 0}, size()});
auto newSize = (is_top()) ? Size{size().width(), size().height() - 16} : size(); // if top(), then there is the info bar at the bottom, so leave space for it
top_view->set_parent_rect({{0, 0}, newSize});
focus();
set_dirty();
@@ -758,14 +750,30 @@ void NavigationView::handle_autostart() {
"nav"sv,
{{"autostart_app"sv, &autostart_app}}};
if (!autostart_app.empty()) {
if (StartAppByName(autostart_app.c_str())) return;
// if returned false, check for external apps by that name, and try to start it
std::string appwithpath = "/" + apps_dir.string() + "/";
appwithpath += autostart_app;
appwithpath += ".ppma";
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> conv;
std::filesystem::path pth = conv.from_bytes(appwithpath.c_str());
ui::ExternalItemsMenuLoader::run_external_app(*this, pth);
bool app_started = false;
// try innerapp
if (StartAppByName(autostart_app.c_str())) {
app_started = true;
} else {
// try outside app
auto external_items = ExternalItemsMenuLoader::load_external_items(app_location_t::HOME, *this);
for (const auto& item : external_items) {
if (item.text == autostart_app) {
item.on_select();
app_started = true;
break;
}
}
}
if (!app_started) {
display_modal(
"Notice", "Autostart failed:\n" +
autostart_app +
"\nupdate sdcard content\n" +
"and check if .ppma exists");
}
}
}
@@ -775,7 +783,9 @@ static void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc)
for (auto& app : NavigationView::appList) {
if (app.menuLocation == loc) {
grid.add_item({app.displayName, app.iconColor, app.icon,
[&nav, &app]() { nav.push_view(std::unique_ptr<View>(app.viewFactory->produce(nav))); }});
[&nav, &app]() {
i2cdev::I2CDevManager::set_autoscan_interval(0); //if i navigate away from any menu, turn off autoscan
nav.push_view(std::unique_ptr<View>(app.viewFactory->produce(nav))); }});
}
};
}
@@ -872,7 +882,6 @@ SystemMenuView::SystemMenuView(NavigationView& nav)
void SystemMenuView::on_populate() {
add_apps(nav_, *this, HOME);
add_item({"HackRF", Theme::getInstance()->fg_cyan->foreground, &bitmap_icon_hackrf, [this]() { hackrf_mode(nav_); }});
}
@@ -912,6 +921,7 @@ SystemView::SystemView(
} else {
add_child(&info_view);
info_view.refresh();
i2cdev::I2CDevManager::set_autoscan_interval(3); // turn on autoscan in sysmainv
}
this->status_view.set_back_enabled(!this->navigation_view.is_top());
@@ -996,17 +1006,48 @@ void BMPView::focus() {
button_done.focus();
}
BMPView::BMPView(NavigationView& nav) {
BMPView::BMPView(NavigationView& nav)
: nav_(nav) {
add_children({&button_done});
button_done.on_select = [this, &nav](Button&) {
nav.pop();
button_done.on_select = [this](Button&) {
handle_pop();
};
}
void BMPView::paint(Painter&) {
if (!portapack::display.drawBMP2({0, 0}, splash_dot_bmp))
portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, false);
portapack::display.drawBMP({(240 - 230) / 2, (320 - 50) / 2 - 10}, splash_bmp, (const uint8_t[]){0x29, 0x18, 0x16});
}
bool BMPView::on_touch(const TouchEvent event) {
/* the event thing were resolved by HTotoo, talked here https://discord.com/channels/719669764804444213/956561375155589192/1287756910950486027
* the touch screen policy can be better, talked here https://discord.com/channels/719669764804444213/956561375155589192/1198926225897443328
*/
if (!nav_.is_valid()) {
return false;
}
switch (event.type) {
case TouchEvent::Type::Start:
return true;
case TouchEvent::Type::End:
handle_pop();
return true;
default:
break;
}
return false;
}
void BMPView::handle_pop() {
if (nav_.is_valid()) {
nav_.pop();
}
}
/* NotImplementedView ****************************************************/
@@ -1072,7 +1113,7 @@ ModalMessageView::ModalMessageView(
}
void ModalMessageView::paint(Painter& painter) {
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, true);
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, (const uint8_t[]){0, 0, 0});
// Break lines.
auto lines = split_string(message_, '\n');
+5
View File
@@ -50,6 +50,7 @@
#include "sd_card.hpp"
#include "external_app.hpp"
#include "view_factory.hpp"
#include "battery.hpp"
// for incrementing fake date when RTC battery is dead
#define DATE_FILEFLAG u"/SETTINGS/DATE_FILEFLAG"
@@ -343,7 +344,11 @@ class BMPView : public View {
void paint(Painter&) override;
void focus() override;
bool on_touch(const TouchEvent event) override;
void handle_pop();
private:
NavigationView& nav_;
Button button_done{
{240, 0, 1, 1},
""};
+3 -1
View File
@@ -37,6 +37,8 @@ class USBSerial {
void on_channel_closed();
void setEventDispatcher(EventDispatcher* ed) { _eventDispatcher = ed; }
bool serial_connected() { return connected; }
private:
void enable_xtal();
void disable_pll0();
@@ -51,4 +53,4 @@ class USBSerial {
EventDispatcher* _eventDispatcher = NULL;
};
} // namespace portapack
} // namespace portapack
+16 -15
View File
@@ -23,6 +23,7 @@
*/
#include "usb_serial_asyncmsg.hpp"
#include "usb_serial.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
@@ -30,7 +31,7 @@
template <>
void UsbSerialAsyncmsg::asyncmsg<int64_t>(const int64_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -38,7 +39,7 @@ void UsbSerialAsyncmsg::asyncmsg<int64_t>(const int64_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<int32_t>(const int32_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -46,7 +47,7 @@ void UsbSerialAsyncmsg::asyncmsg<int32_t>(const int32_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<int16_t>(const int16_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -54,7 +55,7 @@ void UsbSerialAsyncmsg::asyncmsg<int16_t>(const int16_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<int8_t>(const int8_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -62,7 +63,7 @@ void UsbSerialAsyncmsg::asyncmsg<int8_t>(const int8_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<uint8_t>(const uint8_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -70,7 +71,7 @@ void UsbSerialAsyncmsg::asyncmsg<uint8_t>(const uint8_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<uint16_t>(const uint16_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -78,7 +79,7 @@ void UsbSerialAsyncmsg::asyncmsg<uint16_t>(const uint16_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<uint32_t>(const uint32_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -86,7 +87,7 @@ void UsbSerialAsyncmsg::asyncmsg<uint32_t>(const uint32_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<uint64_t>(const uint64_t& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_dec_int(data).c_str());
@@ -94,7 +95,7 @@ void UsbSerialAsyncmsg::asyncmsg<uint64_t>(const uint64_t& data) {
template <>
void UsbSerialAsyncmsg::asyncmsg<float>(const float& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", to_string_decimal(data, 7).c_str());
@@ -105,7 +106,7 @@ void UsbSerialAsyncmsg::asyncmsg<float>(const float& data) {
template <>
// usage: UsbSerialAsyncmsg::asyncmsg(path);
void UsbSerialAsyncmsg::asyncmsg<std::filesystem::path>(const std::filesystem::path& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
std::string path_str = data.string();
@@ -114,7 +115,7 @@ void UsbSerialAsyncmsg::asyncmsg<std::filesystem::path>(const std::filesystem::p
template <>
void UsbSerialAsyncmsg::asyncmsg<std::filesystem::path::string_type>(const std::filesystem::path::string_type& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
std::string str_data(data.begin(), data.end());
@@ -127,7 +128,7 @@ void UsbSerialAsyncmsg::asyncmsg<std::filesystem::path::string_type>(const std::
template <>
// usage: UsbSerialAsyncmsg::asyncmsg(str);
void UsbSerialAsyncmsg::asyncmsg<std::string>(const std::string& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", data.c_str());
@@ -136,7 +137,7 @@ void UsbSerialAsyncmsg::asyncmsg<std::string>(const std::string& data) {
// string literal AKA char[]
// usage: UsbSerialAsyncmsg::asyncmsg("abc");
void UsbSerialAsyncmsg::asyncmsg(const char* data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", data);
@@ -146,8 +147,8 @@ void UsbSerialAsyncmsg::asyncmsg(const char* data) {
template <>
// usage: UsbSerialAsyncmsg::asyncmsg(true);
void UsbSerialAsyncmsg::asyncmsg<bool>(const bool& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
chprintf((BaseSequentialStream*)&SUSBD1, "%s\r\n", data ? "true" : "false");
}
}
+1 -1
View File
@@ -61,7 +61,7 @@ class UsbSerialAsyncmsg {
// ussgae: UsbSerialAsyncmsg::asyncmsg(vec);
template <typename VECTORCOVER>
void UsbSerialAsyncmsg::asyncmsg(const std::vector<VECTORCOVER>& data) {
if (!portapack::async_tx_enabled) {
if (!portapack::async_tx_enabled || !portapack::usb_serial.serial_connected()) {
return;
}
for (const auto& item : data) {
+21 -3
View File
@@ -939,7 +939,7 @@ static void cmd_gotorientation(BaseSequentialStream* chp, int argc, char* argv[]
}
static void cmd_gotenv(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: gotenv <temperature> [humidity] [pressure] [light]\r\n";
const char* usage = "usage: gotenv <temperature> [humidity] [pressure] [light]\r\n"; // keeping light here too for compatibility
if (argc < 1 || argc > 4) {
chprintf(chp, usage);
return;
@@ -950,8 +950,25 @@ static void cmd_gotenv(BaseSequentialStream* chp, int argc, char* argv[]) {
uint16_t light = 0;
if (argc > 1) humi = atof(argv[1]);
if (argc > 2) pressure = atof(argv[2]);
if (argc > 3) light = strtol(argv[3], NULL, 10);
EnvironmentDataMessage msg{temp, humi, pressure, light};
if (argc > 3) light = strtol(argv[0], NULL, 10);
EnvironmentDataMessage msg{temp, humi, pressure};
EventDispatcher::send_message(msg);
// compatibility:
if (argc > 3) {
LightDataMessage msg{light};
EventDispatcher::send_message(msg);
}
chprintf(chp, "ok\r\n");
}
static void cmd_gotlight(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: gotlight <light_lux>\r\n";
if (argc != 1) {
chprintf(chp, usage);
return;
}
uint16_t light = strtol(argv[0], NULL, 10);
LightDataMessage msg{light};
EventDispatcher::send_message(msg);
chprintf(chp, "ok\r\n");
}
@@ -1191,6 +1208,7 @@ static const ShellCommand commands[] = {
{"gotgps", cmd_gotgps},
{"gotorientation", cmd_gotorientation},
{"gotenv", cmd_gotenv},
{"gotlight", cmd_gotlight},
{"sysinfo", cmd_sysinfo},
{"radioinfo", cmd_radioinfo},
{"pmemreset", cmd_pmemreset},
+15 -12
View File
@@ -314,12 +314,6 @@ endmacro()
set(add_to_firmware TRUE)
### ACARS RX
set(MODE_CPPSRC
proc_acars.cpp
)
DeclareTargets(PACA acars)
### ADS-B RX
@@ -480,12 +474,6 @@ set(MODE_CPPSRC
DeclareTargets(PSIG siggen)
### SSTV TX
set(MODE_CPPSRC
proc_sstvtx.cpp
)
DeclareTargets(PSTX sstvtx)
### Tones
@@ -585,6 +573,15 @@ DeclareTargets(PUSB sd_over_usb)
### Place external app and disabled images below so they don't get added to the firmware
set(add_to_firmware FALSE)
### ACARS RX
set(MODE_CPPSRC
proc_acars.cpp
)
DeclareTargets(PACA acars)
### AFSK RX
set(MODE_CPPSRC
@@ -650,6 +647,12 @@ set(MODE_CPPSRC
DeclareTargets(PPVW protoview)
### SSTV TX
set(MODE_CPPSRC
proc_sstvtx.cpp
)
DeclareTargets(PSTX sstvtx)
### TPMS
+143 -21
View File
@@ -20,18 +20,27 @@
* Boston, MA 02110-1301, USA.
*/
#include "proc_acars.hpp"
#include "portapack_shared_memory.hpp"
#include "proc_acars.hpp"
#include "dsp_fir_taps.hpp"
#include "audio_dma.hpp"
#include "event_m4.hpp"
#define SYN 0x16
#define SOH 0x01
#define STX 0x02
#define ETX 0x83
#define ETB 0x97
#define DLE 0x7f
ACARSProcessor::ACARSProcessor() {
audio::dma::init_audio_out();
decim_0.configure(taps_11k0_decim_0.taps);
decim_1.configure(taps_11k0_decim_1.taps);
packet.clear();
decode_data = 0;
decode_count_bit = 0;
audio_output.configure(false);
baseband_thread.start();
}
@@ -45,6 +54,9 @@ void ACARSProcessor::execute(const buffer_c8_t& buffer) {
/* 38.4kHz, 32 samples */
feed_channel_stats(decimator_out);
auto audio = demod.execute(decimator_out, audio_buffer);
audio_output.write(audio);
for (size_t i = 0; i < decimator_out.count; i++) {
if (mf.execute_once(decimator_out.p[i])) {
clock_recovery(mf.get_output());
@@ -52,24 +64,134 @@ void ACARSProcessor::execute(const buffer_c8_t& buffer) {
}
}
void ACARSProcessor::consume_symbol(
const float raw_symbol) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
// const auto decoded_symbol = acars_decode(sliced_symbol);
// DEBUG
packet.add(sliced_symbol);
if (packet.size() == 256) {
payload_handler(packet);
packet.clear();
}
// packet_builder.execute(decoded_symbol);
void ACARSProcessor::add_bit(uint8_t bit) {
decode_data = decode_data << 1 | bit;
decode_count_bit++;
}
void ACARSProcessor::payload_handler(
const baseband::Packet& packet) {
const ACARSPacketMessage message{packet};
uint16_t ACARSProcessor::update_crc(uint8_t dataByte) {
(void)dataByte;
return 0;
}
void ACARSProcessor::sendDebug() {
// if (curr_state <= 1) return;
message.state = curr_state;
shared_memory.application_queue.push(message);
}
void ACARSProcessor::reset() {
decode_data = 0;
decode_count_bit = 0;
curr_state = WSYN;
message.msg_len = 0;
memset(message.message, 0, 250);
message.crc[0] = 0;
message.crc[1] = 0;
parity_errors = 0;
}
void ACARSProcessor::consume_symbol(const float raw_symbol) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
add_bit(sliced_symbol);
if (curr_state == WSYN && decode_count_bit == 8) {
if ((decode_data & 0xff) == SYN) {
curr_state = SYN2;
decode_data = 0;
decode_count_bit = 0;
} else {
decode_count_bit -= 1; // just drop the first bit
}
return;
}
if (curr_state == SYN2 && decode_count_bit == 8) {
if ((decode_data & 0xff) == SYN) {
curr_state = SOH1;
decode_data = 0;
decode_count_bit = 0;
sendDebug();
return;
}
// here i don't have the right packets. so reset
reset();
}
if (curr_state == SOH1 && decode_count_bit == 8) {
if ((decode_data & 0xff) == SOH) {
reset();
curr_state = TXT;
sendDebug();
return;
}
message.message[0] = (decode_data & 0xff); // debug
reset();
sendDebug();
}
if (curr_state == TXT && decode_count_bit == 8) {
uint8_t ch = (decode_data & 0xff);
message.message[message.msg_len++] = ch;
if (!ParityCheck::parity_check(ch)) {
// parity error
parity_errors++;
if (parity_errors > 4) {
reset(); // too many parity errors, skip packet
sendDebug();
return;
}
}
if (ch == ETX || ch == ETB) {
curr_state = CRC1;
sendDebug();
decode_data = 0;
decode_count_bit = 0;
return;
}
if (message.msg_len > 240) {
reset();
sendDebug();
}
if (message.msg_len > 20 && ch == DLE) {
message.msg_len -= 3;
message.crc[0] = message.message[message.msg_len];
message.crc[1] = message.message[message.msg_len + 1];
curr_state = CRC2;
sendDebug();
// to hack the path:
decode_data = message.crc[1];
} else {
decode_count_bit = 0;
decode_data = 0;
return;
}
}
if (curr_state == CRC1 && decode_count_bit == 8) {
message.crc[0] = (decode_data & 0xff);
curr_state = CRC2;
decode_data = 0;
decode_count_bit = 0;
sendDebug();
}
if (curr_state == CRC2 && decode_count_bit == 8) {
message.crc[1] = (decode_data & 0xff);
// send it to app cpu, and it'll take care of the rest
payload_handler();
reset();
curr_state = END;
decode_data = 0;
decode_count_bit = 0;
sendDebug();
}
if (curr_state == END && decode_count_bit == 8) {
reset();
sendDebug();
}
}
void ACARSProcessor::payload_handler() {
message.state = 255; // to indicate this is an actual payload, not a debug packet
shared_memory.application_queue.push(message);
}
@@ -77,4 +199,4 @@ int main() {
EventDispatcher event_dispatcher{std::make_unique<ACARSProcessor>()};
event_dispatcher.run();
return 0;
}
}
+34 -14
View File
@@ -46,11 +46,15 @@
#include "baseband_packet.hpp"
#include "message.hpp"
#include "dsp_demodulate.hpp"
#include "audio_output.hpp"
#include <cstdint>
#include <cstddef>
#include <bitset>
#include "crc.hpp"
// AIS:
// IN: 2457600/8/8 = 38400
// Offset: 2457600/4 = 614400 (614400/8/8 = 9600)
@@ -101,6 +105,14 @@ constexpr std::array<std::complex<float>, 16> rect_taps_38k4_4k8_1t_2k4_p{{
{4.4194173824e-02f, -4.4194173824e-02f},
}};
typedef enum { WSYN,
SYN2,
SOH1,
TXT,
CRC1,
CRC2,
END } Acarsstate;
class ACARSProcessor : public BasebandProcessor {
public:
ACARSProcessor();
@@ -124,24 +136,32 @@ class ACARSProcessor : public BasebandProcessor {
2400,
{0.0555f},
[this](const float symbol) { this->consume_symbol(symbol); }};
symbol_coding::ACARSDecoder acars_decode{};
/*PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder {
{ 0b011010000110100010000000, 24, 1 }, // SYN, SYN, SOH
{ },
{ 128 },
[this](const baseband::Packet& packet) {
this->payload_handler(packet);
}
};*/
baseband::Packet packet{};
uint16_t update_crc(uint8_t dataByte);
void consume_symbol(const float symbol);
void payload_handler();
void add_bit(uint8_t bit);
void reset();
void sendDebug();
std::array<float, 32> audio{};
const buffer_f32_t audio_buffer{
audio.data(),
audio.size()};
dsp::demodulate::AM demod{};
AudioOutput audio_output{};
Acarsstate curr_state = WSYN;
uint32_t decode_data = 0;
uint8_t decode_count_bit = 0;
ACARSPacketMessage message{};
uint8_t parity_errors = 0;
/* NB: Threads should be the last members in the class definition. */
BasebandThread baseband_thread{
baseband_fs, this, baseband::Direction::Receive, /*auto_start*/ false};
RSSIThread rssi_thread{};
void consume_symbol(const float symbol);
void payload_handler(const baseband::Packet& packet);
};
#endif /*__PROC_ACARS_H__*/
#endif /*__PROC_ACARS_H__*/
-107
View File
@@ -1,107 +0,0 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 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 "acars_packet.hpp"
#include "crc.hpp"
#include <cstdlib>
namespace acars {
size_t Packet::length() const {
return packet_.size();
}
bool Packet::is_valid() const {
return true; // length_valid() && crc_ok();
}
Timestamp Packet::received_at() const {
return packet_.timestamp();
}
uint8_t Packet::block_id() const {
return field_.read(96, 8);
}
std::string Packet::registration_number() const {
std::string result;
result.reserve(7);
const size_t character_length = 8;
for (size_t i = 16; i < (16 + 7 * character_length); i += character_length) {
result += (field_.read(i, character_length) & 0x7F);
}
return result;
}
uint32_t Packet::read(const size_t start_bit, const size_t length) const {
return field_.read(start_bit, length);
}
/*std::string Packet::text(
const size_t start_bit,
const size_t character_count
) const {
std::string result;
result.reserve(character_count);
const size_t character_length = 6;
const size_t end_bit = start_bit + character_count * character_length;
for(size_t i=start_bit; i<end_bit; i+=character_length) {
result += char_to_ascii(field_.read(i, character_length));
}
return result;
}*/
bool Packet::crc_ok() const {
CRCReader field_crc{packet_};
CRC<16> acars_fcs{0x1021, 0x0000, 0x0000};
for (size_t i = 0; i < data_length(); i += 8) {
acars_fcs.process_byte(field_crc.read(i, 8));
}
return (acars_fcs.checksum() == (unsigned)field_crc.read(data_length(), fcs_length));
}
size_t Packet::data_and_fcs_length() const {
return length() - 8;
}
size_t Packet::data_length() const {
return data_and_fcs_length() - fcs_length;
}
bool Packet::length_valid() const {
const size_t extra_bits = data_and_fcs_length() & 7;
if (extra_bits != 0) {
return false;
}
return true;
}
} // namespace acars
-74
View File
@@ -1,74 +0,0 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 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 __ACARS_PACKET_H__
#define __ACARS_PACKET_H__
#include "baseband_packet.hpp"
#include "field_reader.hpp"
#include <cstdint>
#include <cstddef>
#include <string>
namespace acars {
class Packet {
public:
constexpr Packet(
const baseband::Packet& packet)
: packet_{packet},
field_{packet_} {
}
size_t length() const;
bool is_valid() const;
Timestamp received_at() const;
uint8_t block_id() const;
std::string registration_number() const;
uint32_t read(const size_t start_bit, const size_t length) const;
// std::string text(const size_t start_bit, const size_t character_count) const;
bool crc_ok() const;
private:
using Reader = FieldReader<baseband::Packet, BitRemapByteReverse>;
using CRCReader = FieldReader<baseband::Packet, BitRemapNone>;
const baseband::Packet packet_;
const Reader field_;
const size_t fcs_length = 16;
size_t data_and_fcs_length() const;
size_t data_length() const;
bool length_valid() const;
};
} /* namespace acars */
#endif /*__ACARS_PACKET_H__*/
+45 -136
View File
@@ -1,124 +1,26 @@
#include "battery.hpp"
#include "event_m0.hpp"
#include "portapack.hpp"
#include "ads1110.hpp"
#include "max17055.hpp"
// uncomment if you want to emulate batt management system
// #define USE_BATT_EMULATOR
extern I2C portapack::i2c0;
#include "i2cdev_max17055.hpp"
#include "i2cdev_ads1110.hpp"
namespace battery {
constexpr uint32_t BATTERY_UPDATE_INTERVAL = 20000;
BatteryManagement::BatteryModules BatteryManagement::detected_ = BatteryManagement::BATT_NONE;
ads1110::ADS1110 battery_ads1110{portapack::i2c0, 0x48};
max17055::MAX17055 battery_max17055{portapack::i2c0, 0x36};
Thread* BatteryManagement::thread = nullptr;
bool BatteryManagement::calcOverride = false;
void BatteryManagement::detect() {
// try to detect supported modules
detected_ = BATT_NONE;
if (battery_ads1110.detect()) {
battery_ads1110.init();
detected_ = BATT_ADS1110;
return;
}
if (battery_max17055.detect()) {
// battery_max17055.init(); //detect will call this on each "re detect"
detected_ = BATT_MAX17055;
return;
}
// add new supported module detect + init here
#ifdef USE_BATT_EMULATOR
if (detected_ == BATT_NONE) {
detected_ = BATT_EMULATOR;
return;
}
#endif
}
void BatteryManagement::init(bool override) {
calcOverride = override;
detect();
// sets timer to query and broadcats this info
create_thread();
}
// set if the default percentage calculation should be overrided by voltage based one
void BatteryManagement::set_calc_override(bool override) {
calcOverride = override;
}
// sets the values, it the currend module supports it.
void BatteryManagement::getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current) {
if (detected_ == BATT_NONE) {
valid_mask = BATT_VALID_NONE;
return;
} else if (detected_ == BATT_ADS1110) {
battery_ads1110.getBatteryInfo(valid_mask, voltage);
batteryPercentage = calc_percent_voltage(voltage);
return;
} else if (detected_ == BATT_MAX17055) {
battery_max17055.getBatteryInfo(valid_mask, batteryPercentage, voltage, current);
if (calcOverride) {
valid_mask &= ~BATT_VALID_PERCENT; // indicate it is voltage based
batteryPercentage = calc_percent_voltage(voltage);
}
return;
}
// 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;
valid_mask = 7;
return true;
}
#endif
(void)current;
}
uint16_t BatteryManagement::read_register(const uint8_t reg) {
if (detected_ == BATT_MAX17055) {
return battery_max17055.read_register(reg);
}
return 0xFFFF;
}
bool BatteryManagement::write_register(const uint8_t reg, const uint16_t value) {
if (detected_ == BATT_MAX17055) {
return battery_max17055.write_register(reg, value);
}
// Helper function to checkif there is ANY battery management ic present.
bool BatteryManagement::isDetected() {
auto dev = i2cdev::I2CDevManager::get_dev_by_model(I2CDEVMDL_MAX17055);
if (dev) return true;
dev = i2cdev::I2CDevManager::get_dev_by_model(I2CDEVMDL_ADS1110);
if (dev) return true;
return false;
}
uint8_t BatteryManagement::getPercent() {
if (detected_ == BATT_NONE) return 102;
uint8_t validity = 0;
uint8_t batteryPercentage = 0;
uint16_t voltage = 0;
int32_t current = 0;
getBatteryInfo(validity, batteryPercentage, voltage, current);
if ((validity & BATT_VALID_VOLTAGE) != BATT_VALID_VOLTAGE) return 102;
if (calcOverride || ((validity & BATT_VALID_PERCENT) != BATT_VALID_PERCENT)) {
validity &= ~BATT_VALID_PERCENT; // indicate it is voltage based
batteryPercentage = calc_percent_voltage(voltage);
}
return batteryPercentage;
}
uint8_t BatteryManagement::calc_percent_voltage(uint16_t voltage) {
// Calculate the remaining battery percentage
uint8_t batteryPercentage = (float)(voltage - BATTERY_MIN_VOLTAGE) / (float)(BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE) * 100.0;
@@ -127,42 +29,49 @@ uint8_t BatteryManagement::calc_percent_voltage(uint16_t voltage) {
return batteryPercentage;
}
uint16_t BatteryManagement::getVoltage() {
if (detected_ == BATT_NONE) return 0;
uint8_t validity = 0;
uint8_t batteryPercentage = 0;
uint16_t voltage = 0;
int32_t current = 0;
getBatteryInfo(validity, batteryPercentage, voltage, current);
if ((validity & BATT_VALID_VOLTAGE) != BATT_VALID_VOLTAGE) return 0;
return voltage;
// helper function to get data from ANY batt management ic
void BatteryManagement::getBatteryInfo(uint8_t& valid_mask, uint8_t& percent, uint16_t& voltage, int32_t& current) {
auto dev = i2cdev::I2CDevManager::get_dev_by_model(I2CDEVMDL_MAX17055);
if (dev) {
((i2cdev::I2cDev_MAX17055*)dev)->getBatteryInfo(valid_mask, percent, voltage, current);
return;
}
dev = i2cdev::I2CDevManager::get_dev_by_model(I2CDEVMDL_ADS1110);
if (dev) {
voltage = ((i2cdev::I2cDev_ADS1110*)dev)->readVoltage();
percent = calc_percent_voltage(voltage);
valid_mask = 1;
return;
}
valid_mask = 0;
}
msg_t BatteryManagement::timer_fn(void* arg) {
(void)arg;
uint8_t validity = 0;
uint8_t batteryPercentage = 102;
uint16_t voltage = 0;
int32_t current = 0;
chThdSleepMilliseconds(1000); // wait ui for fully load
while (1) {
if (!detected_) {
detect(); // try to detect it again, it maybe disconnected while pp was powered up
chThdSleepMilliseconds(500);
}
if (detected_) {
BatteryManagement::getBatteryInfo(validity, batteryPercentage, voltage, current);
// send local message
BatteryStateMessage msg{validity, batteryPercentage, current >= 0, voltage};
EventDispatcher::send_message(msg);
}
chThdSleepMilliseconds(BATTERY_UPDATE_INTERVAL);
// helper function to get data from ANY batt management ic
uint16_t BatteryManagement::get_cycles() {
auto dev = i2cdev::I2CDevManager::get_dev_by_model(I2CDEVMDL_MAX17055);
if (dev) {
return ((i2cdev::I2cDev_MAX17055*)dev)->getValue("Cycles");
}
return 0;
}
void BatteryManagement::create_thread() {
thread = chThdCreateFromHeap(NULL, 512, NORMALPRIO, BatteryManagement::timer_fn, nullptr);
// helper function to get data from ANY batt management ic
float BatteryManagement::get_tte() {
auto dev = i2cdev::I2CDevManager::get_dev_by_model(I2CDEVMDL_MAX17055);
if (dev) {
return ((i2cdev::I2cDev_MAX17055*)dev)->getValue("TTE");
}
return 0;
}
// helper function to get data from ANY batt management ic
float BatteryManagement::get_ttf() {
auto dev = i2cdev::I2CDevManager::get_dev_by_model(I2CDEVMDL_MAX17055);
if (dev) {
return ((i2cdev::I2cDev_MAX17055*)dev)->getValue("TTF");
}
return 0;
}
} // namespace battery
+9 -21
View File
@@ -23,7 +23,6 @@
#define __BATTERY_H__
#include <cstdint>
#include "ch.h"
namespace battery {
@@ -31,38 +30,27 @@ namespace battery {
#define BATTERY_MAX_VOLTAGE 4170.0
#define BATTERY_DESIGN_CAP 2500
// this will just hold the config and a calculation. also some defines above for the battery management. could be movet to the classes, but it is a bit more readeable i think.
class BatteryManagement {
public:
enum BatteryModules {
BATT_NONE = 0,
BATT_ADS1110 = 1,
BATT_MAX17055 = 2,
BATT_EMULATOR = 254
};
enum BatteryValidMask {
BATT_VALID_NONE = 0,
BATT_VALID_VOLTAGE = 1,
BATT_VALID_CURRENT = 2,
BATT_VALID_PERCENT = 4,
BATT_VALID_CYCLES = 8,
BATT_VALID_TTEF = 16,
};
static void init(bool override = false);
static void detect();
static bool isDetected() { return detected_ != BATT_NONE; }
static BatteryModules detectedModule() { return detected_; }
static void getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current);
static uint16_t getVoltage();
static uint8_t getPercent();
static uint16_t read_register(const uint8_t reg);
static bool write_register(const uint8_t reg, const uint16_t value);
static bool isDetected();
static void set_calc_override(bool override);
static uint8_t calc_percent_voltage(uint16_t); // calculates battery percentage from the voltage
static bool calcOverride; // if set to true, it'll override the battery percent calculation based on current voltage.
static void getBatteryInfo(uint8_t& valid_mask, uint8_t& percent, uint16_t& voltage, int32_t& current);
static uint16_t get_cycles();
static float get_tte();
static float get_ttf();
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
static bool calcOverride; // if set to true, it'll override the battery percent calculation based on current voltage.
};
}; // namespace battery
#endif
+17
View File
@@ -190,4 +190,21 @@ class Adler32 {
}
};
static const unsigned char parity_numbits[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
class ParityCheck {
public:
static bool parity_check(uint8_t ch, uint8_t pbitpos = 1) {
return ((parity_numbits[ch] & pbitpos) != 0);
}
};
#endif /*__CRC_H__*/
+15
View File
@@ -29,6 +29,21 @@ void I2C::stop() {
i2cStop(_driver);
}
bool I2C::probe(i2caddr_t addr, systime_t timeout) {
i2cAcquireBus(_driver);
chSysLock();
_driver->errors = I2CD_NO_ERROR;
_driver->state = I2C_ACTIVE_TX;
msg_t rdymsg = i2c_lld_master_transmit_timeout(_driver, addr, nullptr, 0, nullptr, 0, timeout);
if (rdymsg == RDY_TIMEOUT)
_driver->state = I2C_LOCKED;
else
_driver->state = I2C_READY;
chSysUnlock();
i2cReleaseBus(_driver);
return (rdymsg == RDY_OK);
}
bool I2C::transfer(
const address_t slave_address,
const uint8_t* const data_tx,
+1 -1
View File
@@ -59,7 +59,7 @@ class I2C {
void start(const I2CConfig& config);
void stop();
bool probe(i2caddr_t addr, systime_t timeout = TIME_INFINITE);
bool receive(
const address_t slave_address,
uint8_t* const data,
@@ -19,53 +19,60 @@
* Boston, MA 02110-1301, USA.
*/
#include "ads1110.hpp"
#include "i2cdev_ads1110.hpp"
#include "utility.hpp"
#include <algorithm>
#include <cstdint>
namespace battery {
namespace ads1110 {
namespace i2cdev {
void ADS1110::init() {
if (!detected_) {
detected_ = detect();
}
if (detected_) {
bool I2cDev_ADS1110::init(uint8_t addr_) {
if (addr_ != I2CDEV_ADS1110_ADDR_1) return false;
addr = addr_;
model = I2CDEVMDL_ADS1110;
query_interval = BATTERY_WIDGET_REFRESH_INTERVAL;
if (detect()) {
// Set the configuration register
write(0x8C);
return true;
}
return false;
}
bool ADS1110::detect() {
bool I2cDev_ADS1110::detect() {
uint8_t data[3];
if (bus.receive(bus_address, data, 3)) {
if (i2cbus.receive(addr, 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);
void I2cDev_ADS1110::update() {
uint16_t voltage = readVoltage();
uint8_t batteryPercentage = battery::BatteryManagement::calc_percent_voltage(voltage);
// send local message
BatteryStateMessage msg{1, batteryPercentage, false, voltage};
EventDispatcher::send_message(msg);
}
bool I2cDev_ADS1110::write(const uint8_t value) {
return i2cbus.transmit(addr, &value, 1);
}
// returns the batt voltage in mV
uint16_t ADS1110::readVoltage() {
uint16_t I2cDev_ADS1110::readVoltage() {
// Read the conversion result
uint8_t data[3];
if (!bus.receive(bus_address, data, 3)) {
if (!i2cbus.receive(addr, 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;
@@ -115,10 +122,4 @@ uint16_t ADS1110::readVoltage() {
return (uint16_t)voltage;
}
void ADS1110::getBatteryInfo(uint8_t& valid_mask, uint16_t& voltage) {
voltage = readVoltage();
valid_mask = 1; // BATT_VALID_VOLTAGE
}
} /* namespace ads1110 */
} // namespace battery
} /* namespace i2cdev */
@@ -19,39 +19,29 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef __ADS1110_H__
#define __ADS1110_H__
#ifndef __I2CDEV_ADS1110_H__
#define __I2CDEV_ADS1110_H__
#include <cstdint>
#include <array>
#include <string>
#include "battery.hpp"
#include "i2cdevmanager.hpp"
#include "i2c_pp.hpp"
namespace battery {
namespace ads1110 {
namespace i2cdev {
using address_t = uint8_t;
class ADS1110 {
class I2cDev_ADS1110 : public I2cDev {
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_; }
bool init(uint8_t addr_) override;
void update() override;
uint16_t readVoltage();
void getBatteryInfo(uint8_t& valid_mask, uint16_t& voltage);
private:
I2C& bus;
const I2C::address_t bus_address;
bool detected_;
bool write(const uint8_t value);
bool detect();
};
} /* namespace ads1110 */
} // namespace battery
#endif /* __ADS1110_H__ */
} /* namespace i2cdev */
#endif /* __I2CDEV_ADS1110_H__ */
+97
View File
@@ -0,0 +1,97 @@
/*
* 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 "i2cdev_bh1750.hpp"
#include "utility.hpp"
#include <algorithm>
#include <cstdint>
namespace i2cdev {
#define BH1750_CONV_FACTOR 1.2
#define BH1750_MODE 0x13
bool I2cDev_BH1750::init(uint8_t addr_) {
if (addr_ != I2CDEV_BH1750_ADDR_1) return false;
addr = addr_;
model = I2CDECMDL_BH1750;
query_interval = 3;
// power up
uint8_t tmp = 0x01;
if (!i2c_write(nullptr, 0, &tmp, 1)) {
return false;
}
chThdSleepMilliseconds(10);
// set mode
tmp = BH1750_MODE;
if (!i2c_write(nullptr, 0, &tmp, 1)) {
return false;
}
chThdSleepMilliseconds(10);
/*
// unused, since default
// mtreg
tmp = (0b01000 << 3) | (69 >> 5);
if (!i2c_write(nullptr, 0, &tmp, 1)) {
return false;
}
chThdSleepMilliseconds(10);
tmp = (0b011 << 5) | (69 & 0b11111);
if (!i2c_write(nullptr, 0, &tmp, 1)) {
return false;
}
chThdSleepMilliseconds(10);
// mode
tmp = BH1750_MODE;
if (!i2c_write(nullptr, 0, &tmp, 1)) {
return false;
}
chThdSleepMilliseconds(10);
*/
return true;
}
void I2cDev_BH1750::update() {
uint16_t light = readLight();
LightDataMessage msg{light};
EventDispatcher::send_message(msg);
}
uint16_t I2cDev_BH1750::readLight() {
float level = -1.0;
uint8_t tmp[2];
// Read two bytes from the sensor, which are low and high parts of the sensor value
if (i2c_read(nullptr, 0, tmp, 2)) {
uint16_t t = (tmp[0] << 8) | tmp[1];
level = t;
}
if (level != -1.0) {
// Convert raw value to lux
level /= BH1750_CONV_FACTOR;
} else
return 0;
return level;
}
} /* namespace i2cdev */
+43
View File
@@ -0,0 +1,43 @@
/*
* 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 __I2CDEV_BH1750_H__
#define __I2CDEV_BH1750_H__
#include <cstdint>
#include <array>
#include <string>
#include "battery.hpp"
#include "i2cdevmanager.hpp"
namespace i2cdev {
using address_t = uint8_t;
class I2cDev_BH1750 : public I2cDev {
public:
bool init(uint8_t addr_) override;
void update() override;
uint16_t readLight();
};
} /* namespace i2cdev */
#endif /* __I2CDEV_BH1750_H__ */
+188
View File
@@ -0,0 +1,188 @@
/*
* 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 "i2cdev_bmx280.hpp"
namespace i2cdev {
bool I2cDev_BMX280::init(uint8_t addr_) {
if (addr_ != I2CDEV_BMX280_ADDR_1 && addr_ != I2CDEV_BMX280_ADDR_2) return false;
addr = addr_; // store the addr so i2c write / read will use this.
model = I2C_DEVMDL::I2CDEVMDL_BMP280; // set the device model!!!!!!!!!!!!!!!!!!
query_interval = 5; // set update interval in sec
uint8_t reg = BME280_REG_CHIPID; // register
uint8_t tmp = 0; // value. will save fw space, but harder to read code. so read comments
i2c_read(&reg, 1, &tmp, 1); // read chip id to tmp
if (tmp != CHIP_ID_BMP280 && tmp != CHIP_ID_BME280) return false; // this is not BME280 or BMP280, so skip
if (tmp == CHIP_ID_BME280) model = I2C_DEVMDL::I2CDEVMDL_BME280; // update dev model, since this driver can handle 2 type of models
// here we can be "sure" this is a bmp280, so init it
// soft reset the ic
reg = BME280_REG_RESET;
tmp = RESET_KEY;
i2c_write(&reg, 1, &tmp, 1);
chThdSleepMilliseconds(10); // wait to get awake
uint8_t timeout = 0; // wait for calibration data load
while (is_reading_calib()) {
timeout++;
if (timeout > 200) return false; // timeout, bad device
chThdSleepMilliseconds(10);
}
read_coeff();
set_sampling();
chThdSleepMilliseconds(50);
return true;
}
void I2cDev_BMX280::update() {
float temp = read_temperature(); // internal data gathering from the device.
float pressure = read_pressure();
float hum = read_humidity();
EnvironmentDataMessage msg{temp, hum, pressure}; // create the system message
EventDispatcher::send_message(msg); // and send it
}
/*
INTERNAL FUNCTIONS
*/
bool I2cDev_BMX280::is_reading_calib() {
uint8_t const rStatus = read8_1(BMX280_REG_STATUS);
return (rStatus & (1 << 0)) != 0;
}
void I2cDev_BMX280::read_coeff() {
_dig_T1 = read16_LE_1(BMX280_REG_DIG_T1);
_dig_T2 = readS16_LE_1(BMX280_REG_DIG_T2);
_dig_T3 = readS16_LE_1(BMX280_REG_DIG_T3);
_dig_P1 = read16_LE_1(BMX280_REG_DIG_P1);
_dig_P2 = readS16_LE_1(BMX280_REG_DIG_P2);
_dig_P3 = readS16_LE_1(BMX280_REG_DIG_P3);
_dig_P4 = readS16_LE_1(BMX280_REG_DIG_P4);
_dig_P5 = readS16_LE_1(BMX280_REG_DIG_P5);
_dig_P6 = readS16_LE_1(BMX280_REG_DIG_P6);
_dig_P7 = readS16_LE_1(BMX280_REG_DIG_P7);
_dig_P8 = readS16_LE_1(BMX280_REG_DIG_P8);
_dig_P9 = readS16_LE_1(BMX280_REG_DIG_P9);
if (model == I2C_DEVMDL::I2CDEVMDL_BME280) {
_dig_H1 = read8_1(BME280_REG_DIG_H1);
_dig_H2 = readS16_LE_1(BME280_REG_DIG_H2);
_dig_H3 = read8_1(BME280_REG_DIG_H3);
_dig_H4 = ((int8_t)read8_1(BME280_REG_DIG_H4) << 4) | (read8_1(BME280_REG_DIG_H4 + 1) & 0xF);
_dig_H5 = ((int8_t)read8_1(BME280_REG_DIG_H5 + 1) << 4) | (read8_1(BME280_REG_DIG_H5) >> 4);
_dig_H6 = (int8_t)read8_1(BME280_REG_DIG_H6);
}
}
void I2cDev_BMX280::set_sampling() {
//
write8_1(BMX280_REG_CTRL_MEAS, BMX280_MODE_SLEEP);
write8_1(BMX280_REG_CONFIG, (uint8_t)((BMX280_STANDBY_MS_0_5 << 5) | (BMX280_FILTER_OFF << 2)));
if (model == I2C_DEVMDL::I2CDEVMDL_BME280) write8_1(BME280_REG_CTRL_HUM, BMX280_SAMPLING_X16);
write8_1(BMX280_REG_CTRL_MEAS, (uint8_t)((BMX280_SAMPLING_X16 << 5) | (BMX280_SAMPLING_X16 << 2) | BMX280_MODE_NORMAL));
}
float I2cDev_BMX280::read_temperature() {
int32_t var1, var2;
int32_t adc_T = read24_1(BMX280_REG_TEMP);
if (adc_T == 0x800000) // value in case temp measurement was disabled
return 0;
adc_T >>= 4;
var1 = (int32_t)((adc_T / 8) - ((int32_t)_dig_T1 * 2));
var1 = (var1 * ((int32_t)_dig_T2)) / 2048;
var2 = (int32_t)((adc_T / 16) - ((int32_t)_dig_T1));
var2 = (((var2 * var2) / 4096) * ((int32_t)_dig_T3)) / 16384;
_t_fine = var1 + var2; // + t_fine_adjust;
int32_t T = (_t_fine * 5 + 128) / 256;
return (float)T / 100;
}
float I2cDev_BMX280::read_pressure() {
int64_t var1, var2, var3, var4;
// readTemperature(); // must be done first to get t_fine
int32_t adc_P = read24_1(BMX280_REG_PRESS);
if (adc_P == 0x800000) // value in case pressure measurement was disabled
return 0;
adc_P >>= 4;
var1 = ((int64_t)_t_fine) - 128000;
var2 = var1 * var1 * (int64_t)_dig_P6;
var2 = var2 + ((var1 * (int64_t)_dig_P5) * 131072);
var2 = var2 + (((int64_t)_dig_P4) * 34359738368);
var1 = ((var1 * var1 * (int64_t)_dig_P3) / 256) +
((var1 * ((int64_t)_dig_P2) * 4096));
var3 = ((int64_t)1) * 140737488355328;
var1 = (var3 + var1) * ((int64_t)_dig_P1) / 8589934592;
if (var1 == 0) {
return 0; // avoid exception caused by division by zero
}
var4 = 1048576 - adc_P;
var4 = (((var4 * 2147483648) - var2) * 3125) / var1;
var1 = (((int64_t)_dig_P9) * (var4 / 8192) * (var4 / 8192)) /
33554432;
var2 = (((int64_t)_dig_P8) * var4) / 524288;
var4 = ((var4 + var1 + var2) / 256) + (((int64_t)_dig_P7) * 16);
float P = var4 / 256.0;
return P / 100;
}
float I2cDev_BMX280::read_humidity() {
if (model != I2C_DEVMDL::I2CDEVMDL_BME280) return 0;
int32_t var1, var2, var3, var4, var5;
// readTemperature(); // must be done first to get t_fine
int32_t adc_H = read16_1(BME280_REG_HUM);
if (adc_H == 0x8000) // value in case humidity measurement was disabled
return 0;
var1 = _t_fine - ((int32_t)76800);
var2 = (int32_t)(adc_H * 16384);
var3 = (int32_t)(((int32_t)_dig_H4) * 1048576);
var4 = ((int32_t)_dig_H5) * var1;
var5 = (((var2 - var3) - var4) + (int32_t)16384) / 32768;
var2 = (var1 * ((int32_t)_dig_H6)) / 1024;
var3 = (var1 * ((int32_t)_dig_H3)) / 2048;
var4 = ((var2 * (var3 + (int32_t)32768)) / 1024) + (int32_t)2097152;
var2 = ((var4 * ((int32_t)_dig_H2)) + 8192) / 16384;
var3 = var5 * var2;
var4 = ((var3 / 32768) * (var3 / 32768)) / 128;
var5 = var3 - ((var4 * ((int32_t)_dig_H1)) / 16);
var5 = (var5 < 0 ? 0 : var5);
var5 = (var5 > 419430400 ? 419430400 : var5);
uint32_t H = (uint32_t)(var5 / 4096);
return (float)H / 1024.0;
}
} // namespace i2cdev
+133
View File
@@ -0,0 +1,133 @@
/*
* 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.
*/
/*
DEAR DEVS THIS IS AN EXAMPLE FILE FOR i2C COMMUNICATION
YOU have to derive your class from I2cDev and override the init(), and the update()
The update() query the device, and send the corresponding Message to the system with the data.
The init() must check the device if it is really that this driver meant to handle, and fully set the device up. If all ok, set the device's model to the corresponting enum value from "i2cdevlist.hpp" and return true. Othervise false, so next driver can check it. (since multiple different devices manufactured with the same addr)
You can create custom functions, that can be called from any app that identifies the device and casts the I2cDev to your class.
This can be checked by query the 'model' variable.
Steps:
- Add this new module name in the i2cdevlist.hpp's enum (to the end)
- Create a hpp and cpp. (in the init SET THE MODULE to the prev enum value you created). If the filename is i2cdev_*.cpp it'll be added to cmake automatically.
- Add this header to i2cdevmanager.cpp, and add an if statement and the init code to the I2CDevManager::found() function. (see examples)
- Compile and test.
- Create a PR.
Notes: try to create a minimal code, to save FW space.
*/
#ifndef __I2CDEV_BMP280_H__
#define __I2CDEV_BMP280_H__
#include "i2cdevmanager.hpp"
namespace i2cdev {
// Register defines
#define BMX280_REG_DIG_T1 0x88 //!< Temperature coefficient register
#define BMX280_REG_DIG_T2 0x8A //!< Temperature coefficient register
#define BMX280_REG_DIG_T3 0x8C //!< Temperature coefficient register
#define BMX280_REG_DIG_P1 0x8E //!< Pressure coefficient register
#define BMX280_REG_DIG_P2 0x90 //!< Pressure coefficient register
#define BMX280_REG_DIG_P3 0x92 //!< Pressure coefficient register
#define BMX280_REG_DIG_P4 0x94 //!< Pressure coefficient register
#define BMX280_REG_DIG_P5 0x96 //!< Pressure coefficient register
#define BMX280_REG_DIG_P6 0x98 //!< Pressure coefficient register
#define BMX280_REG_DIG_P7 0x9A //!< Pressure coefficient register
#define BMX280_REG_DIG_P8 0x9C //!< Pressure coefficient register
#define BMX280_REG_DIG_P9 0x9E //!< Pressure coefficient register
#define BME280_REG_DIG_H1 0xA1 //!< Humidity coefficient register
#define BME280_REG_DIG_H2 0xE1 //!< Humidity coefficient register
#define BME280_REG_DIG_H3 0xE3 //!< Humidity coefficient register
#define BME280_REG_DIG_H4 0xE4 //!< Humidity coefficient register
#define BME280_REG_DIG_H5 0xE5 //!< Humidity coefficient register
#define BME280_REG_DIG_H6 0xE7 //!< Humidity coefficient register
#define BME280_REG_CHIPID 0xD0 //!< Chip ID register
#define BME280_REG_RESET 0xE0 //!< Reset register
#define BME280_REG_CTRL_HUM 0xF2 //!< BME280: Control humidity register
#define BMX280_REG_STATUS 0XF3 //!< Status register
#define BMX280_REG_CTRL_MEAS 0xF4 //!< Control measure register
#define BMX280_REG_CONFIG 0xF5 //!< Config register
#define BMX280_REG_PRESS 0xF7 //!< Pressure data register
#define BMX280_REG_TEMP 0xFA //!< Temperature data register
#define BME280_REG_HUM 0xFD //!< Humidity data register
// Bit defines
#define CHIP_ID_BMP280 0x58 //!< BMP280 chip ID
#define CHIP_ID_BME280 0x60 //!< BME280 chip ID
#define RESET_KEY 0xB6 //!< Reset value for reset register
#define STATUS_IM_UPDATE 0 //!< im_update bit in status register
#define BMX280_SAMPLING_X4 0b011
#define BMX280_SAMPLING_X16 0b101
#define BMX280_FILTER_OFF 0
#define BMX280_STANDBY_MS_0_5 0
#define BMX280_MODE_NORMAL 0b11
#define BMX280_MODE_SLEEP 0b00
class I2cDev_BMX280 : public I2cDev {
public:
bool init(uint8_t addr_) override; // sets the addr to our local variable, set the model, try to init the module, and only return true if it is really that module, and inited ok
void update() override; // query the module for recent data, and send it to the system via the corresponding Message
private:
// driver specific stuff
void read_coeff();
void set_sampling();
float read_temperature();
float read_pressure();
float read_humidity();
bool is_reading_calib();
uint16_t _dig_T1 = 0;
int16_t _dig_T2 = 0;
int16_t _dig_T3 = 0;
uint16_t _dig_P1 = 0;
int16_t _dig_P2 = 0;
int16_t _dig_P3 = 0;
int16_t _dig_P4 = 0;
int16_t _dig_P5 = 0;
int16_t _dig_P6 = 0;
int16_t _dig_P7 = 0;
int16_t _dig_P8 = 0;
int16_t _dig_P9 = 0;
uint8_t _dig_H1 = 0;
int16_t _dig_H2 = 0;
uint8_t _dig_H3 = 0;
int16_t _dig_H4 = 0;
int16_t _dig_H5 = 0;
int8_t _dig_H6 = 0;
int32_t _t_fine = 0;
};
} // namespace i2cdev
#endif
+635
View File
@@ -0,0 +1,635 @@
/*
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 "i2cdev_max17055.hpp"
#include "battery.hpp"
#include "utility.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>
#include <algorithm>
#include <cstdint>
namespace i2cdev {
const I2cDev_MAX17055::RegisterEntry I2cDev_MAX17055::entries[] = {
{"Status", 0x00, "", 1, false, "", false, 0, true, false, false, 0, false},
{"VAlrtTh", 0x01, "", 1, false, "", false, 0, true, false, false, 0, false},
{"TAlrtTh", 0x02, "", 1, false, "", false, 0, true, false, false, 0, false},
{"SAlrtTh", 0x03, "", 1, false, "", false, 0, true, false, false, 0, false},
{"AtRate", 0x04, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false},
{"RepCap", 0x05, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false},
{"RepSOC", 0x06, "percent", 0.00390625, false, "%", true, 6, true, false, false, 0, false},
{"Age", 0x07, "percent", 0.00390625, false, "%", true, 6, true, false, false, 0, false},
{"Temp", 0x08, "temperature", 0.00390625, true, "C", true, 6, true, false, false, 0, false},
{"VCell", 0x09, "voltage", 0.000078125, false, "V", true, 9, true, false, false, 0, false},
{"Current", 0x0A, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false},
{"AvgCurrent", 0x0B, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false},
{"QResidual", 0x0C, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false},
{"MixSOC", 0x0D, "percent", 0.00390625, false, "%", true, 6, true, false, false, 0, false},
{"AvSOC", 0x0E, "percent", 0.00390625, false, "%", true, 6, true, false, false, 0, false},
{"MixCap", 0x0F, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false},
{"FullCapRep", 0x10, "capacity", 0.5, false, "mAh", true, 1, true, true, false, 0, false},
{"TTE", 0x11, "time", 0.0015625, false, "hr", true, 6, true, false, false, 0, false},
{"QRTable00", 0x12, "model", 1, false, "", false, 0, true, true, false, 0, false},
{"FullSocThr", 0x13, "model", 0.00390625, false, "%", true, 6, true, false, false, 0, false},
{"RCell", 0x14, "resistance", 0.244140625, false, "mOhms", false, 6, true, false, false, 0, false},
{"Reserved", 0x15, "", 0.244140625, false, "mOhms", false, 6, true, false, false, 0, true},
{"AvgTA", 0x16, "temperature", 0.00390625, true, "C", true, 6, true, false, false, 0, false},
{"Cycles", 0x17, "cycles", 0.01, false, "Cycles", false, 2, true, true, false, 0, false},
{"DesignCap", 0x18, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false},
{"AvgVCell", 0x19, "voltage", 0.000078125, false, "V", true, 9, true, false, false, 0, false},
{"MaxMinTemp", 0x1A, "", 1, false, "", false, 0, true, false, false, 0, false},
{"MaxMinVolt", 0x1B, "", 1, false, "", false, 0, true, false, false, 0, false},
{"MaxMinCurr", 0x1C, "", 1, false, "", false, 0, true, false, false, 0, false},
{"Config", 0x1D, "model", 1, false, "", false, 0, true, false, false, 0, false},
{"IChgTerm", 0x1E, "model", 0.15625, true, "mA", true, 5, true, false, false, 0, false},
{"AvCap", 0x1F, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false},
{"TTF", 0x20, "time", 0.0015625, false, "hr", true, 6, true, false, false, 0, false},
{"DevName", 0x21, "", 1, false, "", false, 0, true, false, false, 0, false},
{"QRTable10", 0x22, "model", 1, false, "", false, 0, true, true, false, 0, false},
{"FullCapNom", 0x23, "capacity", 0.5, false, "mAh", true, 1, true, true, false, 0, false},
{"Reserved", 0x24, "", 0.00390625, true, "C", true, 6, true, false, false, 0, true},
{"Reserved", 0x25, "", 0.00390625, true, "C", true, 6, true, false, false, 0, true},
{"Reserved", 0x26, "", 0.00390625, true, "C", true, 6, true, false, false, 0, true},
{"AIN", 0x27, "temperature", 0.0015259, false, "%", true, 6, true, false, false, 0, false},
{"LearnCfg", 0x28, "model", 1, false, "", false, 0, true, false, false, 0, false},
{"FilterCfg", 0x29, "model", 1, false, "", false, 0, true, false, false, 0, false},
{"RelaxCfg", 0x2A, "model", 1, false, "", false, 0, true, false, false, 0, false},
{"MiscCfg", 0x2B, "model", 1, false, "", false, 0, true, false, false, 0, false},
{"TGain", 0x2C, "temperature", 1, false, "", false, 0, true, false, false, 0, false},
{"TOff", 0x2D, "temperature", 1, false, "", false, 0, true, false, false, 0, false},
{"CGain", 0x2E, "current", 0.09765625, true, "%", true, 8, true, false, false, 0, false},
{"COff", 0x2F, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false},
{"Reserved", 0x30, "", 0.00125, false, "V", true, 5, true, false, false, 0, true},
{"Reserved", 0x31, "", 0.005, true, "mA", true, 3, true, false, false, 0, true},
{"QRTable20", 0x32, "model", 1, false, "", false, 0, true, true, false, 0, false},
{"Reserved", 0x33, "", 0.0015625, false, "hr", true, 6, true, true, false, 0, true},
{"DieTemp", 0x34, "temperature", 0.00390625, true, "C", true, 6, true, false, false, 0, false},
{"FullCap", 0x35, "model", 0.5, false, "mAh", true, 1, true, false, false, 0, false},
{"Reserved", 0x36, "", 0.15625, true, "mA", true, 5, true, false, false, 0, true},
{"Reserved", 0x37, "led", 1, false, "", false, 0, true, true, false, 0, false},
{"RComp0", 0x38, "model", 1, false, "", false, 0, true, true, false, 0, false},
{"TempCo", 0x39, "model", 1, false, "", false, 0, true, true, false, 0, false},
{"VEmpty", 0x3A, "model", 0.000078125, false, "", false, 1, true, false, false, 0, false},
{"Reserved", 0x3B, "", 0.15625, true, "mA", true, 5, true, false, false, 0, true},
{"Reserved", 0x3C, "", 0.000976563, false, "s", true, 6, true, false, false, 0, true},
{"FStat", 0x3D, "", 1, false, "", false, 0, true, false, false, 0, false},
{"Timer", 0x3E, "time", 4.88E-05, false, "hr", true, 7, true, false, false, 0, false},
{"ShdnTimer", 0x3F, "", 0.000366, false, "hr", true, 6, true, false, false, 0, false},
{"UserMem1", 0x40, "led", 1, false, "", false, 0, true, false, false, 0, false},
{"Reserved", 0x41, "", 1, false, "", false, 0, true, false, false, 0, true},
{"QRTable30", 0x42, "model", 0.15625, false, "", false, 0, true, true, false, 0, false},
{"RGain", 0x43, "", 1, false, "", false, 0, true, false, false, 0, false},
{"Reserved", 0x44, "", 0.000078125, false, "V", true, 9, true, false, false, 0, true},
{"dQAcc", 0x45, "capacity", 16, false, "mAh", true, 0, true, true, false, 0, false},
{"dPAcc", 0x46, "percent", 0.0625, false, "%", true, 4, true, true, false, 0, false},
{"Reserved", 0x47, "", 0.00390625, true, "%", true, 6, true, true, false, 0, true},
{"Reserved", 0x48, "", 0.00390625, false, "%", true, 6, true, true, false, 0, true},
{"ConvgCfg", 0x49, "", 1, false, "", false, 0, true, false, false, 0, false},
{"VFRemCap", 0x4A, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false},
{"Reserved", 0x4B, "", 1, false, "", false, 0, true, false, false, 0, false},
{"Reserved", 0x4C, "", 0.5, true, "mAh", true, 1, true, false, false, 0, true},
{"QH", 0x4D, "capacity", 0.5, true, "mAh", true, 1, true, false, false, 0, false},
{"Reserved", 0x4E, "", 7.63E-06, false, "mAh", true, 8, true, false, false, 0, true},
{"Reserved", 0x4F, "", 0.5, false, "mAh", true, 1, true, false, false, 0, true},
{"Status2", 0xB0, "", 1, false, "", false, 0, true, false, false, 0, false},
{"Power", 0xB1, "", 0.8, true, "", false, 1, true, false, false, 0, false},
{"ID", 0xB2, "", 1, false, "", false, 0, true, false, false, 0, false},
{"AvgPower", 0xB3, "", 0.8, true, "", false, 1, true, false, false, 0, false},
{"IAlrtTh", 0xB4, "", 1, false, "", false, 0, true, false, false, 0, false},
{"TTFCfg", 0xB5, "", 1, false, "", false, 0, true, false, false, 0, false},
{"CVMixCap", 0xB6, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false},
{"CVHalfTime", 0xB7, "time", 0.000195313, false, "hr", true, 6, true, false, false, 0, false},
{"CGTempCo", 0xB8, "", 1, false, "", false, 0, true, false, false, 0, false},
{"Curve", 0xB9, "", 1, false, "", false, 0, true, false, false, 0, false},
{"HibCfg", 0xBA, "", 1, false, "", false, 0, true, false, false, 0, false},
{"Config2", 0xBB, "", 1, false, "", false, 0, true, false, false, 0, false},
{"VRipple", 0xBC, "voltage", 9.77E-06, false, "V", true, 8, true, false, false, 0, false},
{"RippleCfg", 0xBD, "", 1, false, "", false, 0, true, false, false, 0, false},
{"TimerH", 0xBE, "time", 3.2, false, "hr", true, 1, true, false, false, 0, false},
{"Reserved", 0xBF, "", 0.00390625, false, "%", true, 6, true, false, false, 0, true},
{"Rsense", 0xD0, "", 1, false, "", false, 0, true, false, false, 0, false},
{"ScOcvLim", 0xD1, "", 1, false, "", false, 0, true, false, false, 0, false},
{"Reserved", 0xD2, "", 1, false, "", false, 0, true, false, false, 0, true},
{"SOCHold", 0xD3, "", 1, false, "", false, 0, true, false, false, 0, false},
{"MaxPeakPower", 0xD4, "", 0.8, true, "", false, 0, true, false, false, 0, false},
{"SusPeakPower", 0xD5, "", 0.8, true, "", false, 0, true, false, false, 0, false},
{"PackResistance", 0xD6, "", 0.244141063, false, "", false, 0, true, false, false, 0, false},
{"SysResistance", 0xD7, "", 0.244141063, false, "", false, 0, true, false, false, 0, false},
{"MinSysVoltage", 0xD8, "", 0.000078125, false, "", false, 0, true, false, false, 0, false},
{"MPPCurrent", 0xD9, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false},
{"SPPCurrent", 0xDA, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false},
{"ModelCfg", 0xDB, "", 1, false, "", false, 0, true, false, false, 0, false},
{"AtQResidual", 0xDC, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false},
{"AtTTE", 0xDD, "time", 0.0015625, false, "hr", true, 6, true, false, false, 0, false},
{"AtAvSOC", 0xDE, "percent", 0.00390625, false, "%", true, 6, true, false, false, 0, false},
{"AtAvCap", 0xDF, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false},
{"Reserved", 0xE0, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xE1, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xE2, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xE3, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xE4, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xE5, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xE6, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xE7, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xE8, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xE9, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xEA, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xEB, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xEC, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xED, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xEE, "voltage", 0.000078125, false, "V", true, 9, true, false, false, 0, true},
{"Reserved", 0xEF, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xF0, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xF1, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xF2, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xF3, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xF4, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xF5, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xF6, "", 0.00390625, false, "%", true, 6, true, false, false, 0, true},
{"Reserved", 0xF7, "", 0.001, true, "s", true, 3, true, false, false, 0, true},
{"Reserved", 0xF8, "", 0.003051758, true, "C", true, 6, true, false, false, 0, true},
{"Reserved", 0xF9, "", 0.00015625, false, "V", true, 6, true, false, false, 0, true},
{"Reserved", 0xFA, "", 0.15625, true, "mA", true, 5, true, false, false, 0, true},
{"Reserved", 0xFB, "", 0.000078125, false, "V", true, 9, true, false, false, 0, true},
{"Reserved", 0xFC, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xFD, "", 1, false, "", false, 0, true, false, false, 0, true},
{"Reserved", 0xFE, "", 0.000078125, false, "V", true, 9, true, false, false, 0, true},
{"Reserved", 0xFF, "", 0.00390625, false, "%", true, 6, true, false, false, 0, true},
};
void I2cDev_MAX17055::update() {
uint8_t validity = 0;
uint8_t batteryPercentage = 102;
uint16_t voltage = 0;
int32_t current = 0;
getBatteryInfo(validity, batteryPercentage, voltage, current);
// send local message
BatteryStateMessage msg{validity, batteryPercentage, current >= 0, voltage};
EventDispatcher::send_message(msg);
}
bool I2cDev_MAX17055::init(uint8_t addr_) {
if (addr_ != I2CDEV_MAX17055_ADDR_1) return false;
addr = addr_;
model = I2CDEVMDL_MAX17055;
query_interval = BATTERY_WIDGET_REFRESH_INTERVAL;
if (detect()) {
if (needsInitialization()) {
// First-time or POR initialization
full_reset_and_init();
} else {
// Subsequent boot
partialInit();
}
partialInit();
// statusClear(); I am not sure if this should be here or not
return true;
}
return false;
}
bool I2cDev_MAX17055::full_reset_and_init() {
if (!soft_reset()) {
return false;
}
if (!initialize_custom_parameters()) {
return false;
}
if (!load_custom_parameters()) {
return false;
}
if (!clear_por()) {
return false;
}
return true;
}
bool I2cDev_MAX17055::soft_reset() {
return write_register(0x0BB, 0x0000);
}
bool I2cDev_MAX17055::initialize_custom_parameters() {
if (!write_register(0xD0, 0x03E8)) return false; // Unknown register, possibly related to battery profile
if (!write_register(0xDB, 0x0000)) return false; // ModelCfg
if (!write_register(0x05, 0x0000)) return false; // RepCap
if (!write_register(0x18, 0x1388)) return false; // DesignCap
if (!write_register(0x45, 0x009C)) return false; // dQAcc
if (!write_register(0x1E, 0x03C0)) return false; // IChgTerm
if (!write_register(0x3A, 0x9661)) return false; // VEmpty
if (!write_register(0x60, 0x0090)) return false; // Unknown register
if (!write_register(0x46, 0x0561)) return false; // dPAcc
if (!write_register(0xDB, 0x8000)) return false; // ModelCfg
if (!write_register(0x40, 0x0001)) return false; // Set user mem to 1
return true;
}
bool I2cDev_MAX17055::load_custom_parameters() {
uint16_t hib_cfg = read_register(0xBA);
if (!write_register(0xBA, 0x0000)) return false; // Disable hibernate mode
if (!write_register(0x60, 0x0000)) return false; // Unknown register
// Wait for the model to be loaded
for (int i = 0; i < 10; i++) {
uint16_t model_cfg = read_register(0xDB);
if ((model_cfg & 0x8000) == 0) {
break;
}
chThdSleepMilliseconds(10);
}
if (!write_register(0xBA, hib_cfg)) return false; // Restore hibernate config
return true;
}
bool I2cDev_MAX17055::clear_por() {
uint16_t status = read_register(0x00);
status &= ~(1 << 1);
return write_register(0x00, status);
}
bool I2cDev_MAX17055::needsInitialization() {
uint16_t UserMem1 = read_register(0x40);
if (UserMem1 == 0) {
return true;
}
return false;
}
void I2cDev_MAX17055::partialInit() {
// Only update necessary volatile settings
setHibCFG(0x0000); // If you always want hibernation disabled
// Add any other volatile settings that need updating
}
bool I2cDev_MAX17055::reset_learned() {
// this if for reset all the learned parameters by ic
// the full inis should do this
full_reset_and_init();
return true;
}
bool I2cDev_MAX17055::detect() {
// Read the DevName register (0x21)
uint16_t dev_name = read_register(0x21);
// The DevName register should return 0x4010 for MAX17055
if (dev_name == 0x4010) {
return true;
}
// If DevName doesn't match, try reading Status register as a fallback
uint16_t status = read_register(0x00);
if (status != 0xFFFF && status != 0x0000) {
return true;
}
return false;
}
const I2cDev_MAX17055::RegisterEntry* I2cDev_MAX17055::findEntry(const char* name) const {
for (const auto& entry : entries) {
if (std::strcmp(entry.name, name) == 0) {
return &entry;
}
}
return nullptr;
}
uint16_t I2cDev_MAX17055::read_register(const uint8_t reg) {
const std::array<uint8_t, 1> tx{reg};
std::array<uint8_t, 2> rx{0x00, 0x00};
i2cbus.transmit(addr, tx.data(), tx.size());
i2cbus.receive(addr, rx.data(), rx.size());
// Combine the two bytes into a 16-bit value
// little-endian format (LSB first)
return static_cast<uint16_t>((rx[1] << 8) | rx[0]);
}
bool I2cDev_MAX17055::write_register(const uint8_t reg, const uint16_t value) {
std::array<uint8_t, 3> tx;
tx[0] = reg;
tx[1] = value & 0xFF; // Low byte
tx[2] = (value >> 8) & 0xFF; // High byte
bool success = i2cbus.transmit(addr, tx.data(), tx.size());
chThdSleepMilliseconds(1);
return success;
}
void I2cDev_MAX17055::getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current) {
// Read Status Register
uint16_t status = read_register(0x00);
voltage = averageMVoltage();
if ((status == 0 && voltage == 0) || (status == 0x0002 && voltage == 3600) || (status == 0x0002 && voltage == 0)) {
valid_mask = 0;
return;
}
batteryPercentage = stateOfCharge();
current = instantCurrent();
valid_mask = 31; // BATT_VALID_VOLTAGE + CURRENT + PERCENT + BATT_VALID_CYCLES + BATT_VALID_TTEF
if (battery::BatteryManagement::calcOverride) {
valid_mask &= ~battery::BatteryManagement::BATT_VALID_PERCENT; // indicate it is voltage based
batteryPercentage = battery::BatteryManagement::calc_percent_voltage(voltage);
}
}
float I2cDev_MAX17055::getValue(const char* entityName) {
const RegisterEntry* entry = findEntry(entityName);
if (entry) {
uint16_t raw_value = read_register(entry->address);
float scaled_value;
if (entry->is_signed) {
int16_t signed_value = static_cast<int16_t>(raw_value);
scaled_value = signed_value * entry->scalar;
} else {
scaled_value = raw_value * entry->scalar;
}
return scaled_value;
}
return 0; // Return 0 if entry not found
}
uint16_t I2cDev_MAX17055::averageMVoltage(void) {
return static_cast<uint16_t>(getValue("AvgVCell") * 1000.0f); // Convert to millivolts
}
int32_t I2cDev_MAX17055::instantCurrent(void) {
return getValue("Current");
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x0A);
// Convert to signed int16_t (two's complement)
int32_t _Signed_Raw = static_cast<int16_t>(_Measurement_Raw);
int32_t _Value = (_Signed_Raw * 15625) / (__MAX17055_Resistor__ * 100) / 100000;
// End Function
return _Value;
}
uint16_t I2cDev_MAX17055::stateOfCharge(void) {
return getValue("RepSOC");
}
bool I2cDev_MAX17055::setEmptyVoltage(uint16_t _Empty_Voltage) {
// Calculate the new VE_Empty value (upper 9 bits)
uint16_t ve_empty = ((_Empty_Voltage * 100) / 10) & 0xFF80;
// Read the current register value
uint16_t current_value = read_register(0x3A);
// Preserve the lower 7 bits (VR_Empty) and combine with new VE_Empty
uint16_t new_value = (ve_empty & 0xFF80) | (current_value & 0x007F);
// Write the new value back to the register
return write_register(0x3A, new_value);
}
bool I2cDev_MAX17055::setRecoveryVoltage(uint16_t _Recovery_Voltage) {
// Calculate the new VR_Empty value (lower 7 bits)
uint16_t vr_empty = (_Recovery_Voltage * 25) & 0x007F; // 40mV per bit, 25 = 1000/40
// Read the current register value
uint16_t current_value = read_register(0x3A);
// Preserve the upper 9 bits (VE_Empty) and combine with new VR_Empty
uint16_t new_value = (current_value & 0xFF80) | vr_empty;
// Write the new value back to the register
return write_register(0x3A, new_value);
}
bool I2cDev_MAX17055::setMinVoltage(uint16_t _Minimum_Voltage) {
uint16_t current_value = read_register(0x01);
uint16_t min_voltage_raw = (_Minimum_Voltage * 50) & 0x00FF; // 20mV per bit, 50 = 1000/20
uint16_t new_value = (current_value & 0xFF00) | min_voltage_raw;
return write_register(0x01, new_value);
}
bool I2cDev_MAX17055::setMaxVoltage(uint16_t _Maximum_Voltage) {
uint16_t current_value = read_register(0x01);
uint16_t max_voltage_raw = ((_Maximum_Voltage * 50) & 0x00FF) << 8; // 20mV per bit, 50 = 1000/20
uint16_t new_value = (current_value & 0x00FF) | max_voltage_raw;
return write_register(0x01, new_value);
}
bool I2cDev_MAX17055::setMaxCurrent(uint16_t _Maximum_Current) {
uint16_t current_value = read_register(0xB4);
uint16_t max_current_raw = ((_Maximum_Current * 25) & 0x00FF) << 8; // 40mV per bit, 25 = 1000/40
uint16_t new_value = (current_value & 0x00FF) | max_current_raw;
return write_register(0xB4, new_value);
}
bool I2cDev_MAX17055::setChargeTerminationCurrent(uint16_t _Charge_Termination_Current) {
float lsb_mA = 1.5625 / (__MAX17055_Resistor__ * 1000); // Convert to mA
uint16_t ichgterm_value = static_cast<uint16_t>(round(_Charge_Termination_Current / lsb_mA));
return write_register(0x1E, ichgterm_value);
}
bool I2cDev_MAX17055::setDesignCapacity(const uint16_t _Capacity) {
uint16_t raw_cap = (uint16_t)_Capacity * 2;
return write_register(0x18, raw_cap);
}
bool I2cDev_MAX17055::setFullCapRep(const uint16_t _Capacity) {
uint16_t raw_cap = _Capacity * 2; // 0.5mAh per LSB
return write_register(0x10, raw_cap);
}
bool I2cDev_MAX17055::setFullCapNom(const uint16_t _Capacity) {
uint16_t raw_cap = _Capacity * 2; // 0.5mAh per LSB
return write_register(0x23, raw_cap);
}
bool I2cDev_MAX17055::setRepCap(const uint16_t _Capacity) {
uint16_t raw_cap = _Capacity * 2; // 0.5mAh per LSB
return write_register(0x05, raw_cap);
}
bool I2cDev_MAX17055::setMinSOC(uint8_t _Minimum_SOC) {
uint16_t current_value = read_register(0x03);
uint16_t min_soc_raw = ((_Minimum_SOC * 256) / 100) & 0x00FF;
uint16_t new_value = (current_value & 0xFF00) | min_soc_raw;
return write_register(0x03, new_value);
}
bool I2cDev_MAX17055::setMaxSOC(uint8_t _Maximum_SOC) {
uint16_t current_value = read_register(0x03);
uint16_t max_soc_raw = (((_Maximum_SOC * 256) / 100) & 0x00FF) << 8;
uint16_t new_value = (current_value & 0x00FF) | max_soc_raw;
return write_register(0x03, new_value);
}
bool I2cDev_MAX17055::setMinTemperature(uint8_t _Minimum_Temperature) {
uint16_t current_value = read_register(0x02);
uint16_t min_temp_raw = (uint8_t)_Minimum_Temperature;
uint16_t new_value = (current_value & 0xFF00) | min_temp_raw;
return write_register(0x02, new_value);
}
bool I2cDev_MAX17055::setMaxTemperature(uint8_t _Maximum_Temperature) {
uint16_t current_value = read_register(0x02);
uint16_t max_temp_raw = ((uint8_t)_Maximum_Temperature) << 8;
uint16_t new_value = (current_value & 0x00FF) | max_temp_raw;
return write_register(0x02, new_value);
}
bool I2cDev_MAX17055::setModelCfg(const uint8_t _Model_ID) {
uint16_t model_cfg = 0x0400; // Set Charge Voltage (bit 10)
// Set Battery Model
switch (_Model_ID) {
case 0: // Default model
break;
case 2: // EZ model
model_cfg |= 0x0020;
break;
case 6: // Short EZ model
model_cfg |= 0x0060;
break;
default:
return false; // Invalid model ID
}
return write_register(0xDB, model_cfg);
}
bool I2cDev_MAX17055::setHibCFG(const uint16_t _Config) {
return write_register(0xBA, _Config);
}
void I2cDev_MAX17055::config(void) {
uint16_t config1 = 0x0000;
uint16_t config2 = 0x0618; // Default value: 0b00011000 00000110
// Set Configuration bits [Config1]
if (MAX17055_Ber) config1 |= 0x0001;
if (MAX17055_Bei) config1 |= 0x0002;
if (MAX17055_Aen) config1 |= 0x0004;
if (MAX17055_FTHRM) config1 |= 0x0008;
if (MAX17055_ETHRM) config1 |= 0x0010;
if (MAX17055_COMMSH) config1 |= 0x0040;
if (MAX17055_SHDN) config1 |= 0x0080;
if (MAX17055_Tex) config1 |= 0x0100;
if (MAX17055_Ten) config1 |= 0x0200;
if (MAX17055_AINSH) config1 |= 0x0400;
if (MAX17055_IS) config1 |= 0x0800;
if (MAX17055_VS) config1 |= 0x1000;
if (MAX17055_TS) config1 |= 0x2000;
if (MAX17055_SS) config1 |= 0x4000;
if (MAX17055_TSel) config1 |= 0x8000;
// Set Configuration bits [Config2]
if (MAX17055_CPMode) config2 |= 0x0002;
if (MAX17055_LDMDL) config2 |= 0x0020;
if (MAX17055_TAIrtEN) config2 |= 0x0040;
if (MAX17055_dSOCen) config2 |= 0x0080;
if (MAX17055_DPEn) config2 |= 0x1000;
if (MAX17055_AtRateEn) config2 |= 0x2000;
// Write configurations to registers
write_register(0x1D, config1);
write_register(0xBB, config2);
}
bool I2cDev_MAX17055::statusClear() {
// Clear all bits in the Status register (0x00)
return write_register(0x00, 0x0000);
}
bool bitRead(uint8_t value, uint8_t bit) {
return (value >> bit) & 0x01;
}
bool I2cDev_MAX17055::statusControl(const uint8_t _Status) {
// Read Status Register (0x00)
uint16_t status_register = read_register(0x00);
// Control for Status
switch (_Status) {
case MAX17055_POR:
return bitRead(status_register & 0xFF, 1);
case MAX17055_IMin:
return bitRead(status_register & 0xFF, 2);
case MAX17055_IMax:
return bitRead(status_register & 0xFF, 6);
case MAX17055_SOC_Change:
return bitRead(status_register & 0xFF, 7);
case MAX17055_Bat_Status:
return bitRead(status_register & 0xFF, 3);
case MAX17055_VMin:
return bitRead(status_register >> 8, 0);
case MAX17055_VMax:
return bitRead(status_register >> 8, 4);
case MAX17055_TMin:
return bitRead(status_register >> 8, 1);
case MAX17055_TMax:
return bitRead(status_register >> 8, 5);
case MAX17055_SOC_Min:
return bitRead(status_register >> 8, 2);
case MAX17055_SOC_Max:
return bitRead(status_register >> 8, 6);
case MAX17055_Bat_Insert:
return bitRead(status_register >> 8, 3);
case MAX17055_Bat_Remove:
return bitRead(status_register >> 8, 7);
default:
return false;
}
}
} /* namespace i2cdev */
@@ -25,9 +25,9 @@
#include <cstdint>
#include <array>
#include <string>
#include "battery.hpp"
#include "i2c_pp.hpp"
#include "i2cdevmanager.hpp"
#define MAX17055_POR 0
#define MAX17055_IMin 1
@@ -83,7 +83,7 @@
// Define Termination Current
#ifndef __MAX17055_Termination_Current__
#define __MAX17055_Termination_Current__ 0.1 // Termination Current
#define __MAX17055_Termination_Current__ 200 // Termination Current
#endif
// Define Minimum Temperature
@@ -248,54 +248,59 @@
#define MAX17055_AtRateEn 0
#endif
namespace battery {
namespace max17055 {
namespace i2cdev {
using address_t = uint8_t;
class MAX17055 {
class I2cDev_MAX17055 : public I2cDev {
public:
constexpr MAX17055(I2C& bus, const I2C::address_t bus_address)
: bus(bus), bus_address(bus_address), detected_(false) {}
struct RegisterEntry {
const char* name;
uint8_t address;
const char* type;
float scalar;
bool is_signed;
const char* unit;
bool abbr_units;
int resolution;
bool is_user;
bool is_save_restore;
bool is_nv;
uint16_t por_data;
bool is_read_only;
};
static const RegisterEntry entries[];
static constexpr size_t entries_count = 144;
void init();
bool detect();
bool isDetected() const { return detected_; }
uint16_t readVoltage();
uint8_t readPercentage();
void getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current);
uint16_t instantVoltage(void);
uint16_t averageVoltage(void);
uint16_t emptyVoltage(void);
uint16_t recoveryVoltage(void);
int32_t instantCurrent(void);
int32_t averageCurrent(void);
uint16_t stateOfCharge(void);
uint16_t averageStateOfCharge(void);
uint16_t instantCapacity(void);
uint16_t designCapacity(void);
uint16_t fullCapacity(void);
uint16_t icTemperature(void);
uint16_t timeToEmpty(void);
uint16_t timeToFull(void);
uint16_t batteryAge(void);
uint16_t chargeCycle(void);
bool statusControl(const uint8_t _Status);
void statusClear(void);
uint16_t chargeTerminationCurrent(void);
uint16_t read_register(const uint8_t reg);
bool write_register(const uint8_t reg, const uint16_t value);
private:
I2C& bus;
const I2C::address_t bus_address;
bool detected_ = false;
bool init(uint8_t addr_) override;
void update() override;
bool detect();
bool readRegister(uint8_t reg, uint16_t& value);
bool readMultipleRegister(uint8_t reg, uint8_t* data, uint8_t length, bool endTransmission);
bool writeMultipleRegister(uint8_t reg, const uint8_t* data, uint8_t length);
void getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current);
bool reset_learned();
float getValue(const char* entityName);
uint16_t averageMVoltage(void);
int32_t instantCurrent(void);
uint16_t stateOfCharge(void);
private:
const RegisterEntry* findEntry(const char* name) const;
bool needsInitialization();
void partialInit();
bool statusControl(const uint8_t _Status);
bool statusClear();
bool full_reset_and_init();
bool soft_reset();
bool clear_por();
bool initialize_custom_parameters();
bool load_custom_parameters();
bool setEmptyVoltage(uint16_t _Empty_Voltage);
bool setRecoveryVoltage(uint16_t _Recovery_Voltage);
@@ -316,6 +321,5 @@ class MAX17055 {
void config(void);
};
} /* namespace max17055 */
} // namespace battery
} /* namespace i2cdev */
#endif /* __MAX17055_H__ */
+62
View File
@@ -0,0 +1,62 @@
/*
* 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 "i2cdev_sht3x.hpp"
namespace i2cdev {
bool I2cDev_SHT3x::init(uint8_t addr_) {
if (addr_ != I2CDEV_SHT3X_ADDR_1 && addr_ != I2CDEV_SHT3X_ADDR_2) return false;
addr = addr_; // store the addr
model = I2C_DEVMDL::I2CDEVMDL_SHT3X; // set the device model!!!!!!!!!!!!!!!!!!
query_interval = 5; // set update interval in sec
chThdSleepMilliseconds(50);
uint8_t tmp[2]; // command buffer
tmp[0] = 0x30;
tmp[1] = 0x66;
if (!i2c_write(nullptr, 0, tmp, 2)) return false; // heater off
chThdSleepMilliseconds(50);
tmp[0] = 0x22;
tmp[1] = 0x36;
if (!i2c_write(nullptr, 0, tmp, 2)) return false; // conti, 2mps
chThdSleepMilliseconds(50);
return true;
}
void I2cDev_SHT3x::update() {
float temp = 0;
float hum = 0;
uint8_t cmd[2];
cmd[0] = 0xe0;
cmd[1] = 0;
uint8_t res[6];
i2c_read(cmd, 2, res, 6);
uint16_t temptick = res[0] << 8 | res[1];
uint16_t humtick = res[3] << 8 | res[4];
temp = -45 + (((float)temptick * 175.0) / 65535.0);
hum = (100.0 * (float)humtick) / 65535.0;
EnvironmentDataMessage msg{temp, hum}; // create the system message
EventDispatcher::send_message(msg); // and send it
}
} // namespace i2cdev
+40
View File
@@ -0,0 +1,40 @@
/*
* 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 __I2CDEV_SHT3X_H__
#define __I2CDEV_SHT3X_H__
#include "i2cdevmanager.hpp"
namespace i2cdev {
class I2cDev_SHT3x : public I2cDev {
public:
bool init(uint8_t addr_) override; // sets the addr to our local variable, set the model, try to init the module, and only return true if it is really that module, and inited ok
void update() override; // query the module for recent data, and send it to the system via the corresponding Message
private:
float read_temperature();
float read_humidity();
};
} // namespace i2cdev
#endif
+53
View File
@@ -0,0 +1,53 @@
/*
* 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 __I2CDEVLIST_H__
#define __I2CDEVLIST_H__
/*
DEAR DEVS: Put your new driver to this enum's end. Also consider using define to store it's address.
If the same driver can handle multiple devices, with different data, should use different names. Like BMP280 + BME280. If the data is the same across multiple devices, can use the same name. Like SHT3x (sht30, sht31, ..)
*/
enum I2C_DEVMDL {
I2CDEVMDL_NOTSET, // i2c dev present, but no driver for it
I2CDEVMDL_MAX17055,
I2CDEVMDL_ADS1110,
I2CDEVMDL_SHT3X,
I2CDEVMDL_BMP280,
I2CDEVMDL_BME280,
I2CDECMDL_BH1750,
};
#define I2CDEV_BMX280_ADDR_1 0x76
#define I2CDEV_BMX280_ADDR_2 0x77
#define I2CDEV_SHT3X_ADDR_1 0x44
#define I2CDEV_SHT3X_ADDR_2 0x45
#define I2CDEV_MAX17055_ADDR_1 0x36
#define I2CDEV_ADS1110_ADDR_1 0x48
#define I2CDEV_BH1750_ADDR_1 0x23
// this will be the update interval for battery management ic's:
#define BATTERY_WIDGET_REFRESH_INTERVAL 10
#endif
+334
View File
@@ -0,0 +1,334 @@
/*
* 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 <algorithm>
#include "i2cdevmanager.hpp"
/*
DEAR DEVS.
Include your devices headers here:
*/
#include "i2cdev_bmx280.hpp"
#include "i2cdev_sht3x.hpp"
#include "i2cdev_max17055.hpp"
#include "i2cdev_ads1110.hpp"
#include "i2cdev_bh1750.hpp"
namespace i2cdev {
// statics
uint16_t I2CDevManager::scan_interval = 0;
bool I2CDevManager::force_scan = false;
Thread* I2CDevManager::thread;
std::vector<I2DevListElement> I2CDevManager::devlist;
Mutex I2CDevManager::mutex_list{};
/*
DEAR DEVELOPERS!
IF YOU WANT TO ADD NEW DERIVERS, PUT IT'S I2C ADDRESS AND INIT PART HERE.
THE INIT MUST RETURN FALSE, WHEN THE DEVICE NOT MATCH OR NOT WORKING.
*/
bool I2CDevManager::found(uint8_t addr) {
// check if present already
for (size_t i = 0; i < devlist.size(); i++) {
if (devlist[i].addr == addr) return false;
}
// try to find a suitable driver
I2DevListElement item;
item.addr = addr;
/*
DEAR DEVS
Put your driver's init code here. ALLWAYS check the !item.dev, if any other driver already took it. Also check the addr if it suits your module. (also need additional checks in the init() code)
*/
if (!item.dev && (addr == I2CDEV_BMX280_ADDR_1 || addr == I2CDEV_BMX280_ADDR_2)) { // check if device is already taken, and i can handle the address
item.dev = std::make_unique<I2cDev_BMX280>();
if (!item.dev->init(addr)) item.dev = nullptr; // if not inited, reset it's instance, and let other handlers try
}
if (!item.dev && (addr == I2CDEV_SHT3X_ADDR_1 || addr == I2CDEV_SHT3X_ADDR_2)) {
item.dev = std::make_unique<I2cDev_SHT3x>();
if (!item.dev->init(addr)) item.dev = nullptr;
}
if (!item.dev && (addr == I2CDEV_MAX17055_ADDR_1)) {
item.dev = std::make_unique<I2cDev_MAX17055>();
if (!item.dev->init(addr)) item.dev = nullptr;
}
if (!item.dev && (addr == I2CDEV_ADS1110_ADDR_1)) {
item.dev = std::make_unique<I2cDev_ADS1110>();
if (!item.dev->init(addr)) item.dev = nullptr;
}
if (!item.dev && (addr == I2CDEV_BH1750_ADDR_1)) {
item.dev = std::make_unique<I2cDev_BH1750>();
if (!item.dev->init(addr)) item.dev = nullptr;
}
// if can't find any driver, add it too with empty, so we won't try to init it again and again
devlist.push_back(std::move(item));
return true;
}
/*
FROM HERE YOU SHOULDN'T WRITE ANYTHING IF YOU JUST IMPLEMENT A NEW DRIVER
(maybe maximum i2c_rewd / write helpers)
*/
void I2cDev::set_update_interval(uint8_t interval) {
query_interval = interval;
}
uint8_t I2cDev::get_update_interval() {
return query_interval;
}
void I2cDev::got_error() {
errcnt++;
if (errcnt >= 5) need_del = true; // too many errors. remove dev from list. may be re-discovered and re inited
}
void I2cDev::got_success() {
errcnt = 0;
}
/*
I2C read / write functions and helpers.
*/
bool I2cDev::i2c_read(uint8_t* reg, uint8_t reg_size, uint8_t* data, uint8_t bytes) {
if (bytes == 0) return false;
if (reg_size > 0 && reg) i2cbus.transmit(addr, reg, reg_size);
bool ret = i2cbus.receive(addr, data, bytes);
if (!ret)
got_error();
else
got_success();
return ret;
}
bool I2cDev::i2c_write(uint8_t* reg, uint8_t reg_size, uint8_t* data, uint8_t bytes) {
// Check if there's any data to write
if (bytes == 0) return false;
// Create a new buffer to hold both reg and data
uint8_t total_size = reg_size + bytes;
uint8_t* buffer = new uint8_t[total_size];
// Copy the register data into the buffer
if (reg_size > 0 && reg) {
memcpy(buffer, reg, reg_size);
}
// Copy the data into the buffer after the register data
memcpy(buffer + reg_size, data, bytes);
// Transmit the combined data
bool result = i2cbus.transmit(addr, buffer, total_size);
// Clean up the dynamically allocated buffer
delete[] buffer;
if (!result)
got_error();
else
got_success();
return result;
}
bool I2cDev::write8_1(uint8_t reg, uint8_t data) {
return i2c_write(&reg, 1, &data, 1);
}
uint8_t I2cDev::read8_1(uint8_t reg) {
uint8_t res = 0;
i2c_read(&reg, 1, &res, 1);
return res;
}
uint16_t I2cDev::read16_1(uint8_t reg) {
uint8_t buffer[2];
i2c_read(&reg, 1, buffer, 2);
return uint16_t(buffer[0]) << 8 | uint16_t(buffer[1]);
}
uint32_t I2cDev::read24_1(uint8_t reg) {
uint8_t buffer[3];
i2c_read(&reg, 1, buffer, 2);
return uint32_t(buffer[0]) << 16 | uint32_t(buffer[1]) << 8 | uint32_t(buffer[2]);
}
int16_t I2cDev::readS16_1(uint8_t reg) {
return (int16_t)read16_1(reg);
}
uint16_t I2cDev::read16_LE_1(uint8_t reg) {
uint16_t res = read16_1(reg);
res = (res >> 8) | (res << 8);
return res;
}
int16_t I2cDev::readS16_LE_1(uint8_t reg) {
return (int16_t)read16_LE_1(reg);
}
// END OF i2C communication + helpers
void I2CDevManager::init() {
force_scan = true;
create_thread();
}
void I2CDevManager::manual_scan() {
force_scan = true;
}
void I2CDevManager::set_autoscan_interval(uint16_t interval) {
scan_interval = interval;
}
uint16_t I2CDevManager::get_autoscan_interval() {
return scan_interval;
}
I2cDev* I2CDevManager::get_dev_by_addr(uint8_t addr) {
chMtxLock(&mutex_list);
for (size_t i = 0; i < devlist.size(); i++) {
if (devlist[i].addr == addr) {
chMtxUnlock();
return devlist[i].dev.get();
}
}
chMtxUnlock();
return nullptr;
}
I2cDev* I2CDevManager::get_dev_by_model(I2C_DEVMDL model) {
chMtxLock(&mutex_list);
for (size_t i = 0; i < devlist.size(); i++) {
if (devlist[i].dev && devlist[i].dev->model == model) {
chMtxUnlock();
return devlist[i].dev.get();
}
}
chMtxUnlock();
return nullptr;
}
std::vector<I2C_DEVMDL> I2CDevManager::get_dev_list_by_model() {
std::vector<I2C_DEVMDL> ret;
chMtxLock(&mutex_list);
for (size_t i = 0; i < devlist.size(); i++) {
if (devlist[i].dev) {
ret.push_back(devlist[i].dev->model);
}
}
chMtxUnlock();
return ret;
}
std::vector<uint8_t> I2CDevManager::get_gev_list_by_addr() {
std::vector<uint8_t> ret;
chMtxLock(&mutex_list);
for (size_t i = 0; i < devlist.size(); i++) {
if (devlist[i].addr) {
ret.push_back(devlist[i].addr);
}
}
chMtxUnlock();
return ret;
}
bool I2CDevManager::scan() {
bool changed = false;
std::vector<uint8_t> currList;
for (uint8_t i = 1; i < 128; ++i) {
if (i2cbus.probe(i, 50)) {
chMtxLock(&mutex_list);
changed = changed | found(i);
chMtxUnlock();
currList.push_back(i);
}
chThdSleepMilliseconds(1);
}
// remove those not present
for (size_t i = 0; i < devlist.size(); ++i) {
if (std::find(currList.begin(), currList.end(), devlist[i].addr) == currList.end()) {
// found on our list, but now not discovered, so remove it
devlist[i].addr = 0; // mark to delete
changed = true;
}
}
return changed;
}
void I2CDevManager::create_thread() {
chMtxInit(&mutex_list);
thread = chThdCreateFromHeap(NULL, 2048, NORMALPRIO, I2CDevManager::timer_fn, nullptr);
}
msg_t I2CDevManager::timer_fn(void* arg) {
(void)arg;
uint16_t curr_timer = 0; // seconds since thread start
while (1) {
systime_t start_time = chTimeNow();
bool changed = false;
// check if i2c scan needed
if (force_scan || (scan_interval != 0 && curr_timer % scan_interval == 0)) {
changed = changed | scan();
force_scan = false;
}
for (size_t i = 0; i < devlist.size(); i++) {
if (devlist[i].addr != 0 && devlist[i].dev) {
if ((curr_timer % devlist[i].dev->query_interval) == 0) { // only if it is device's interval
devlist[i].dev->update(); // updates it's data, and broadcasts it. if there is any error it will handle in it, and later we can remove it
}
}
}
// remove all unneeded items
chMtxLock(&mutex_list);
size_t cnt = devlist.size();
devlist.erase(std::remove_if(devlist.begin(), devlist.end(), [](const I2DevListElement& x) {
if (x.addr == 0) return true;
if (x.dev && x.dev->need_del == true) return true; // self destruct on too many errors
return false; // won't remove the unidentified ones, so we can list them, and not trying all the time with them
}),
devlist.end());
chMtxUnlock();
if (cnt != devlist.size()) changed = true;
if (changed) {
I2CDevListChangedMessage msg{};
EventDispatcher::send_message(msg);
}
systime_t end_time = chTimeNow();
systime_t delta = (end_time > start_time) ? end_time - start_time : 100; // wont calculate overflow, just guess.
if (delta > 950) delta = 950; // ensure minimum 50 milli sleep
chThdSleepMilliseconds(1000 - delta); // 1sec timer
++curr_timer;
}
return 0;
}
}; // namespace i2cdev
+104
View File
@@ -0,0 +1,104 @@
/*
* 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 __I2CDEVMANAGER_H__
#define __I2CDEVMANAGER_H__
#include <cstdint>
#include <vector>
#include <memory>
#include "ch.h"
#include "portapack.hpp"
#include "i2c_pp.hpp"
#include "i2cdevlist.hpp"
#include "event_m0.hpp"
#define i2cbus portapack::i2c0
extern I2C portapack::i2c0;
namespace i2cdev {
// The device class. You'll derive your from this. Override init() and update();
class I2cDev {
public:
virtual ~I2cDev(){};
virtual bool init(uint8_t addr); // returns true if it is that that device we are looking for.
virtual void update() = 0; // override this, and you'll be able to query your device and broadcast the result to the system
void set_update_interval(uint8_t interval); // sets the device's update interval in sec. if you change it, don't forget to change back to it's original value after you finished!
uint8_t get_update_interval(); // gets the device's update interval in sec
bool i2c_read(uint8_t* reg, uint8_t reg_size, uint8_t* data, uint8_t bytes); // if want to read without register addr, just set reg_size to 0. this way can read 8, or 16 or 32 bit registers too. reg_size in bytes! returns true on succes. handles the errcnt automatically!
bool i2c_write(uint8_t* reg, uint8_t reg_size, uint8_t* data, uint8_t bytes); // if want to write without register addr, just set reg_size to 0. this way can read 8, or 16 or 32 bit registers too. reg_size in bytes! returns true on succes. handles the errcnt automatically!
// helpers for easier i2c communication
uint8_t read8_1(uint8_t reg);
bool write8_1(uint8_t reg, uint8_t data);
uint16_t read16_1(uint8_t reg);
int16_t readS16_1(uint8_t reg);
uint16_t read16_LE_1(uint8_t reg);
int16_t readS16_LE_1(uint8_t reg);
uint32_t read24_1(uint8_t reg);
bool need_del = false; // device can self destruct, and re-init when new scan discovers it
I2C_DEVMDL model = I2CDEVMDL_NOTSET; // overwrite it in the init()!!!
uint8_t query_interval = 5; // in seconds. can be overriden in init() if necessary
protected:
void got_error(); // i2c communication will call this when communication was not ok. you can call it from any part of your code too.
void got_success(); // i2c communication will call this when the communication was ok. you can call it from any part of your code too.
uint8_t addr = 0; // some devices can have different addresses, so we store what was it wound with
uint8_t errcnt = 0; // error count during communication. if it reaches a threshold set need_del to remove itself from the device list
};
// store for the devices. may not have a driver if not supported
class I2DevListElement {
public:
uint8_t addr = 0; // i2c addr of the device
std::unique_ptr<I2cDev> dev = nullptr; // device driver if any
};
class I2CDevManager {
public:
static void init(); // creates the thread, and sets an one time full scan
static void manual_scan(); // it'll init a forced device scan in the thread's next cycle. (1sec max)
static void set_autoscan_interval(uint16_t interval); // 0 no auto scan, other values: seconds
static uint16_t get_autoscan_interval();
static I2cDev* get_dev_by_addr(uint8_t addr); // caller function needs to cast to the specific device!
static I2cDev* get_dev_by_model(I2C_DEVMDL model); // caller function needs to cast to the specific device!
static std::vector<I2C_DEVMDL> get_dev_list_by_model(); // returns the currently discovered
static std::vector<uint8_t> get_gev_list_by_addr(); // returns the currently discovered
private:
static uint16_t scan_interval;
static bool force_scan; // if set to true, on hte next run it'll do an i2c scan, ONCE
static std::vector<I2DevListElement> devlist;
static bool found(uint8_t addr); // returns true on any new device. also initializes the driver if there is a suitable one
static bool scan(); // return true on any change (delete or found new)
static void create_thread();
static msg_t timer_fn(void* arg);
static Thread* thread;
static Mutex mutex_list;
};
}; // namespace i2cdev
#endif
+20 -6
View File
@@ -344,7 +344,20 @@ void ILI9341::render_box(const ui::Point p, const ui::Size s, const ui::Color* l
}
// RLE_4 BMP loader (delta not implemented)
void ILI9341::drawBMP(const ui::Point p, const uint8_t* bitmap, const bool transparency) {
/* draw transparent, pass transparent color as arg, usage inline anonymous obj
* portapack::display.drawBMP({100, 100}, foo_bmp, (const uint8_t[]){41, 24, 22}); // dec, out of {255, 255, 255}
* portapack::display.drawBMP({100, 100}, foo_bmp, (const uint8_t[]){0x29, 0x18, 0x16}); //hex out of {0xFF, 0xFF, 0xFF}
*
* draw transparent, pass transparent color as arg, usage pass uint8_t[] obj
* const uint8_t c[] = {41, 24, 22}; or const uint8_t c[3] = {0x29, 0x18, 0x16};
* portapack::display.drawBMP({100, 100}, foo_bmp, c);
*
* don't draw transparent, pass nullptr as arg, usage
* portapack::display.drawBMP({100, 100}, foo_bmp, nullptr);
*
* if your image use RLE compress as transparency methods, pass any valid color as arg, it doesn't matter (like the modal bmp. TODO: write RLE transparency generator)
* */
void ILI9341::drawBMP(const ui::Point p, const uint8_t* bitmap, const uint8_t* transparency_color) {
const bmp_header_t* bmp_header = (const bmp_header_t*)bitmap;
uint32_t data_idx;
uint8_t by, c, count, transp_idx = 0;
@@ -359,15 +372,16 @@ void ILI9341::drawBMP(const ui::Point p, const uint8_t* bitmap, const bool trans
data_idx = bmp_header->image_data;
const bmp_palette_t* bmp_palette = (const bmp_palette_t*)&bitmap[bmp_header->BIH_size + 14];
// Convert palette and find pure magenta index (alpha color key)
// Convert palette and find pure magenta index (alpha color key) rgb dec(41,24,22)
for (c = 0; c < 16; c++) {
palette[c] = ui::Color(bmp_palette->color[c].R, bmp_palette->color[c].G, bmp_palette->color[c].B);
if ((bmp_palette->color[c].R == 0xFF) &&
(bmp_palette->color[c].G == 0x00) &&
(bmp_palette->color[c].B == 0xFF)) transp_idx = c;
if (transparency_color &&
(bmp_palette->color[c].R == transparency_color[0]) &&
(bmp_palette->color[c].G == transparency_color[1]) &&
(bmp_palette->color[c].B == transparency_color[2])) transp_idx = c;
}
if (!transparency) {
if (!transparency_color) {
py = bmp_header->height + 16;
do {
by = bitmap[data_idx++];
+1 -1
View File
@@ -62,7 +62,7 @@ class ILI9341 {
const ui::Color background);
void draw_pixel(const ui::Point p, const ui::Color color);
void drawBMP(const ui::Point p, const uint8_t* bitmap, const bool transparency);
void drawBMP(const ui::Point p, const uint8_t* bitmap, const uint8_t* transparency_color);
bool drawBMP2(const ui::Point p, const std::filesystem::path& file);
void render_line(const ui::Point p, const uint8_t count, const ui::Color* line_buffer);
void render_box(const ui::Point p, const ui::Size s, const ui::Color* line_buffer);
-821
View File
@@ -1,821 +0,0 @@
/*
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 "max17055.hpp"
#include "utility.hpp"
#include <cstring>
#include <algorithm>
#include <cstdint>
namespace battery {
namespace max17055 {
void MAX17055::init() {
if (!detected_) {
detected_ = detect();
}
if (detected_) { // check again if it is detected
config();
setHibCFG(0x0000);
// Design Capacity Register
setDesignCapacity(__MAX17055_Design_Capacity__);
// ModelCfg Register
setModelCfg(__MAX17055_Battery_Model__);
// IChgTerm Register
setChargeTerminationCurrent(__MAX17055_Termination_Current__);
// VEmpty Register
setEmptyVoltage(__MAX17055_Empty_Voltage__);
// VRecovery Register
setRecoveryVoltage(__MAX17055_Recovery_Voltage__);
// Set Minimum Voltage
setMinVoltage(__MAX17055_Min_Voltage__);
// Set Maximum Voltage
setMaxVoltage(__MAX17055_Max_Voltage__);
// Set Maximum Current
setMaxCurrent(__MAX17055_Max_Current__);
// Set Minimum SOC
setMinSOC(__MAX17055_Min_SOC__);
// Set Maximum SOC
setMaxSOC(__MAX17055_Max_SOC__);
// Set Minimum Temperature
setMinTemperature(__MAX17055_Min_Temperature__);
// Set Maximum Temperature
setMaxTemperature(__MAX17055_Max_Temperature__);
// Clear Bits
statusClear();
}
}
bool MAX17055::detect() {
uint8_t _MAX17055_Data[2];
// Get Data from IC
if (readMultipleRegister(0x00, _MAX17055_Data, 2, false)) {
detected_ = true;
return true;
}
detected_ = false;
return false;
}
bool bitRead(uint8_t value, uint8_t bit) {
return (value >> bit) & 0x01;
}
void bitSet(uint16_t& value, uint8_t bit) {
value |= (1 << bit);
}
void bitSet(uint8_t& value, uint8_t bit) {
value |= (1 << bit);
}
void bitClear(uint16_t& value, uint8_t bit) {
value &= ~(1 << bit);
}
uint16_t MAX17055::read_register(const uint8_t reg) {
const std::array<uint8_t, 1> tx{reg};
std::array<uint8_t, 2> rx{0x00, 0x00};
bus.transmit(bus_address, tx.data(), tx.size());
bus.receive(bus_address, rx.data(), rx.size());
// Combine the two bytes into a 16-bit value
// little-endian format (LSB first)
return static_cast<uint16_t>((rx[1] << 8) | rx[0]);
}
bool MAX17055::write_register(const uint8_t reg, const uint16_t value) {
std::array<uint8_t, 3> tx;
tx[0] = reg;
tx[1] = value & 0xFF; // Low byte
tx[2] = (value >> 8) & 0xFF; // High byte
bool success = bus.transmit(bus_address, tx.data(), tx.size());
return success;
}
bool MAX17055::readMultipleRegister(uint8_t reg, uint8_t* data, uint8_t length, bool endTransmission) {
if (bus.transmit(bus_address, &reg, 1)) {
if (bus.receive(bus_address, data, length)) {
if (endTransmission) {
// bus.stop(); // Testing if we need this line
// Perform any necessary end transmission actions
// For example, you can use bus.endTransmission()
}
return true;
}
}
return false;
}
bool MAX17055::writeMultipleRegister(uint8_t reg, const uint8_t* data, uint8_t length) {
uint8_t buffer[length + 1];
buffer[0] = reg;
memcpy(buffer + 1, data, length);
if (bus.transmit(bus_address, buffer, length + 1)) {
// Perform any necessary end transmission actions
// For example, you can use bus.endTransmission()
return true;
}
return false;
}
void MAX17055::getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current) {
if (detected_) {
uint16_t status = 0;
// Read Status Register
readMultipleRegister(0x00, (uint8_t*)&status, 2, false);
voltage = averageVoltage();
if ((status == 0 && voltage == 0) || (status == 0x0002 && voltage == 3600) || (status == 0x0002 && voltage == 0)) {
valid_mask = 0;
return;
}
batteryPercentage = stateOfCharge();
current = instantCurrent();
valid_mask = 7; // BATT_VALID_VOLTAGE + CURRENT + PERCENT
} else {
// let's indicate the data is wrong. ui will handle this by display UNK values.
valid_mask = 0;
}
}
bool MAX17055::setEmptyVoltage(uint16_t _Empty_Voltage) {
// Set Voltage Value
uint16_t _Raw_Voltage = ((uint16_t)((uint16_t)_Empty_Voltage * 100) << 7) & 0b1111111110000000;
// Define Data Variable
uint8_t MAX17055_RAW_Data[2];
// Handle Data
MAX17055_RAW_Data[1] = (uint8_t)(_Raw_Voltage >> 8);
MAX17055_RAW_Data[0] = (uint8_t)(_Raw_Voltage & 0x00FF);
// Define Data Variable
uint8_t MAX17055_Current_Data[2];
// Read Current Register
readMultipleRegister(0x3A, MAX17055_Current_Data, 2, true);
// Clear Current Value
MAX17055_Current_Data[0] &= 0b01111111;
MAX17055_Current_Data[1] &= 0b00000000;
// Define Data Variable
uint8_t MAX17055_Handle_Data[2];
// Handle Data
MAX17055_Handle_Data[0] = MAX17055_Current_Data[0] | MAX17055_RAW_Data[0];
MAX17055_Handle_Data[1] = MAX17055_Current_Data[1] | MAX17055_RAW_Data[1];
// Set Register
bool _Result = writeMultipleRegister(0x3A, MAX17055_Handle_Data, 2);
// End Function
return _Result;
}
bool MAX17055::setRecoveryVoltage(uint16_t _Recovery_Voltage) {
// Handle Value
_Recovery_Voltage = _Recovery_Voltage * 1000 / 40;
// Set Voltage Value
uint16_t _Raw_Voltage = ((uint16_t)_Recovery_Voltage);
// Define Data Variable
uint8_t MAX17055_RAW_Data[2];
// Handle Data
MAX17055_RAW_Data[1] = (uint8_t)(_Raw_Voltage >> 8);
MAX17055_RAW_Data[0] = (uint8_t)(_Raw_Voltage & 0x00FF);
// Define Data Variable
uint8_t MAX17055_Current_Data[2];
// Read Current Register
readMultipleRegister(0x3A, MAX17055_Current_Data, 2, true);
// Clear Current Value
MAX17055_Current_Data[0] &= 0b10000000;
MAX17055_Current_Data[1] &= 0b11111111;
// Define Data Variable
uint8_t MAX17055_Handle_Data[2];
// Handle Data
MAX17055_Handle_Data[0] = MAX17055_Current_Data[0] | MAX17055_RAW_Data[0];
MAX17055_Handle_Data[1] = MAX17055_Current_Data[1] | MAX17055_RAW_Data[1];
// Set Register
bool _Result = writeMultipleRegister(0x3A, MAX17055_Handle_Data, 2);
// End Function
return _Result;
}
bool MAX17055::setMinVoltage(uint16_t _Minimum_Voltage) {
// Set Voltage Value
uint8_t _Raw_Voltage = (uint8_t)(_Minimum_Voltage * 1000 / 20);
// Define Data Variable
uint8_t MAX17055_Current_Data[2];
// Read Current Register
readMultipleRegister(0x01, MAX17055_Current_Data, 2, true);
// Set Voltage Value
MAX17055_Current_Data[0] = _Raw_Voltage;
// Set Register
bool _Result = writeMultipleRegister(0x01, MAX17055_Current_Data, 2);
// End Function
return _Result;
}
bool MAX17055::setMaxVoltage(uint16_t _Maximum_Voltage) {
// Set Voltage Value
uint8_t _Raw_Voltage = (uint8_t)(_Maximum_Voltage * 1000 / 20);
// Define Data Variable
uint8_t MAX17055_Current_Data[2];
// Read Current Register
readMultipleRegister(0x01, MAX17055_Current_Data, 2, true);
// Set Voltage Value
MAX17055_Current_Data[1] = _Raw_Voltage;
// Set Register
bool _Result = writeMultipleRegister(0x01, MAX17055_Current_Data, 2);
// End Function
return _Result;
}
bool MAX17055::setMaxCurrent(uint16_t _Maximum_Current) {
// Set Current Value
uint8_t _Raw_Current = (uint8_t)(_Maximum_Current * 1000 / 40);
// Define Data Variable
uint8_t MAX17055_Current_Data[2];
// Read Current Register
readMultipleRegister(0xB4, MAX17055_Current_Data, 2, true);
// Set Current Value
MAX17055_Current_Data[1] = _Raw_Current;
// Set Register
bool _Result = writeMultipleRegister(0xB4, MAX17055_Current_Data, 2);
// End Function
return _Result;
}
bool MAX17055::setChargeTerminationCurrent(uint16_t _Charge_Termination_Current) {
// Handle Raw Data
uint16_t _RAW_Data = (uint16_t)(_Charge_Termination_Current * 1000000 * __MAX17055_Resistor__ / 1.5625);
// Declare Default Data Array
uint8_t _Data[2];
// Set Data Low/High Byte
_Data[0] = ((_RAW_Data & (uint16_t)0x00FF));
_Data[1] = ((_RAW_Data & (uint16_t)0xFF00) >> 8);
// Set Register
bool _Result = writeMultipleRegister(0x1E, _Data, 2);
// End Function
return _Result;
}
bool MAX17055::setDesignCapacity(const uint16_t _Capacity) {
// Set Raw
uint16_t _Raw_Cap = (uint16_t)_Capacity * 2;
// Declare Default Data Array
uint8_t _Data[2];
// Set Data Low/High Byte
_Data[0] = ((_Raw_Cap & (uint16_t)0x00FF));
_Data[1] = ((_Raw_Cap & (uint16_t)0xFF00) >> 8);
// Set Register
bool _Result = writeMultipleRegister(0x18, _Data, 2);
// End Function
return _Result;
}
bool MAX17055::setFullCapRep(const uint16_t _Capacity) {
// Set Raw
uint16_t _Raw_Cap = (uint16_t)_Capacity * 2;
// Declare Default Data Array
uint8_t _Data[2];
// Set Data Low/High Byte
_Data[0] = ((_Raw_Cap & (uint16_t)0x00FF));
_Data[1] = ((_Raw_Cap & (uint16_t)0xFF00) >> 8);
// Set Register
bool _Result = writeMultipleRegister(0x10, _Data, 2);
// End Function
return _Result;
}
bool MAX17055::setFullCapNom(const uint16_t _Capacity) {
// Set Raw
uint16_t _Raw_Cap = (uint16_t)_Capacity * 2;
// Declare Default Data Array
uint8_t _Data[2];
// Set Data Low/High Byte
_Data[0] = ((_Raw_Cap & (uint16_t)0x00FF));
_Data[1] = ((_Raw_Cap & (uint16_t)0xFF00) >> 8);
// Set Register
bool _Result = writeMultipleRegister(0x23, _Data, 2);
// End Function
return _Result;
}
bool MAX17055::setRepCap(const uint16_t _Capacity) {
// Set Raw
uint16_t _Raw_Cap = (uint16_t)_Capacity * 2;
// Declare Default Data Array
uint8_t _Data[2];
// Set Data Low/High Byte
_Data[0] = ((_Raw_Cap & (uint16_t)0x00FF));
_Data[1] = ((_Raw_Cap & (uint16_t)0xFF00) >> 8);
// Set Register
bool _Result = writeMultipleRegister(0x05, _Data, 2);
// End Function
return _Result;
}
bool MAX17055::setMinSOC(uint8_t _Minimum_SOC) {
// Define Data Variable
uint8_t MAX17055_Current_Data[2];
// Read Current Register
readMultipleRegister(0x03, MAX17055_Current_Data, 2, true);
// Scale Value
uint8_t _MinSOC = (_Minimum_SOC / 100) * 255;
// Set Voltage Value
MAX17055_Current_Data[0] = _MinSOC;
// Set Register
bool _Result = writeMultipleRegister(0x03, MAX17055_Current_Data, 2);
// End Function
return _Result;
}
bool MAX17055::setMaxSOC(uint8_t _Maximum_SOC) {
// Define Data Variable
uint8_t MAX17055_Current_Data[2];
// Read Current Register
readMultipleRegister(0x03, MAX17055_Current_Data, 2, true);
// Scale Value
uint8_t _MaxSOC = (_Maximum_SOC / 100) * 255;
// Set Voltage Value
MAX17055_Current_Data[1] = _MaxSOC;
// Set Register
bool _Result = writeMultipleRegister(0x03, MAX17055_Current_Data, 2);
// End Function
return _Result;
}
bool MAX17055::setMinTemperature(uint8_t _Minimum_Temperature) {
// Define Data Variable
uint8_t MAX17055_Current_Data[2];
// Read Current Register
readMultipleRegister(0x02, MAX17055_Current_Data, 2, true);
// Set Voltage Value
MAX17055_Current_Data[0] = _Minimum_Temperature;
// Set Register
bool _Result = writeMultipleRegister(0x02, MAX17055_Current_Data, 2);
// End Function
return _Result;
}
bool MAX17055::setMaxTemperature(uint8_t _Maximum_Temperature) {
// Define Data Variable
uint8_t MAX17055_Current_Data[2];
// Read Current Register
readMultipleRegister(0x02, MAX17055_Current_Data, 2, true);
// Set Voltage Value
MAX17055_Current_Data[1] = _Maximum_Temperature;
// Set Register
bool _Result = writeMultipleRegister(0x02, MAX17055_Current_Data, 2);
// End Function
return _Result;
}
bool MAX17055::setModelCfg(const uint8_t _Model_ID) {
// Declare Variable
uint16_t _Data_Set = 0x00;
// Set Charge Voltage
bitSet(_Data_Set, 10);
// Set Battery Model
if (_Model_ID == 0) {
bitClear(_Data_Set, 4);
bitClear(_Data_Set, 5);
bitClear(_Data_Set, 6);
bitClear(_Data_Set, 7);
}
if (_Model_ID == 2) {
bitClear(_Data_Set, 4);
bitSet(_Data_Set, 5);
bitClear(_Data_Set, 6);
bitClear(_Data_Set, 7);
}
if (_Model_ID == 6) {
bitClear(_Data_Set, 4);
bitSet(_Data_Set, 5);
bitSet(_Data_Set, 6);
bitClear(_Data_Set, 7);
}
// Declare Default Data Array
uint8_t _Data[2];
// Set Data Low/High Byte
_Data[0] = ((_Data_Set & (uint16_t)0x00FF));
_Data[1] = ((_Data_Set & (uint16_t)0xFF00) >> 8);
// Set Register
bool _Result = writeMultipleRegister(0xDB, _Data, 2);
// End Function
return _Result;
}
bool MAX17055::setHibCFG(const uint16_t _Config) {
// Declare Default Data Array
uint8_t _Data[2];
// Set Data Low/High Byte
_Data[0] = ((_Config & (uint16_t)0x00FF));
_Data[1] = ((_Config & (uint16_t)0xFF00) >> 8);
// Set Register
bool _Result = writeMultipleRegister(0xBA, _Data, 2);
// End Function
return _Result;
}
void MAX17055::config(void) {
// Declare Default Data Array
uint8_t _Config1[2];
uint8_t _Config2[2];
// Set Default Data
_Config1[0] = 0b00000000;
_Config1[1] = 0b00000000;
_Config2[0] = 0b00011000;
_Config2[1] = 0b00000110;
// Set Configuration bits [Config1]
if (MAX17055_Ber) bitSet(_Config1[0], 0);
if (MAX17055_Bei) bitSet(_Config1[0], 1);
if (MAX17055_Aen) bitSet(_Config1[0], 2);
if (MAX17055_FTHRM) bitSet(_Config1[0], 3);
if (MAX17055_ETHRM) bitSet(_Config1[0], 4);
if (MAX17055_COMMSH) bitSet(_Config1[0], 6);
if (MAX17055_SHDN) bitSet(_Config1[0], 7);
if (MAX17055_Tex) bitSet(_Config1[1], 0);
if (MAX17055_Ten) bitSet(_Config1[1], 1);
if (MAX17055_AINSH) bitSet(_Config1[1], 2);
if (MAX17055_IS) bitSet(_Config1[1], 3);
if (MAX17055_VS) bitSet(_Config1[1], 4);
if (MAX17055_TS) bitSet(_Config1[1], 5);
if (MAX17055_SS) bitSet(_Config1[1], 6);
if (MAX17055_TSel) bitSet(_Config1[1], 7);
// Set Configuration bits [Config2]
if (MAX17055_CPMode) bitSet(_Config2[0], 1);
if (MAX17055_LDMDL) bitSet(_Config2[0], 5);
if (MAX17055_TAIrtEN) bitSet(_Config2[0], 6);
if (MAX17055_dSOCen) bitSet(_Config2[0], 7);
if (MAX17055_DPEn) bitSet(_Config2[1], 4);
if (MAX17055_AtRateEn) bitSet(_Config2[1], 5);
// Config1 Setting
writeMultipleRegister(0x1D, _Config1, 2);
writeMultipleRegister(0xBB, _Config2, 2);
}
uint16_t MAX17055::instantVoltage(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x09);
// Calculate Measurement
uint16_t _Value = ((uint32_t)_Measurement_Raw * 1.25 / 16);
// End Function
return _Value;
}
uint16_t MAX17055::averageVoltage(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x19);
// Calculate Measurement
uint16_t _Value = ((uint32_t)_Measurement_Raw * 1.25 / 16);
// End Function
return _Value;
}
uint16_t MAX17055::emptyVoltage(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x3A);
_Measurement_Raw = ((_Measurement_Raw & 0xFF80) >> 7);
// Calculate Measurement
uint16_t _Value = ((uint32_t)_Measurement_Raw * 10);
// End Function
return _Value;
}
uint16_t MAX17055::recoveryVoltage(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x3A);
_Measurement_Raw = (_Measurement_Raw & 0x7F);
// Calculate Measurement
uint16_t _Value = ((uint32_t)_Measurement_Raw * 40);
// End Function
return _Value;
}
int32_t MAX17055::instantCurrent(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x0A);
// Convert to signed int16_t (two's complement)
int32_t _Signed_Raw = static_cast<int16_t>(_Measurement_Raw);
int32_t _Value = (_Signed_Raw * 15625) / (__MAX17055_Resistor__ * 100) / 100000;
// End Function
return _Value;
}
int32_t MAX17055::averageCurrent(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x0B);
// Convert to signed int16_t (two's complement)
int32_t _Signed_Raw = static_cast<int16_t>(_Measurement_Raw);
int32_t _Value = (_Signed_Raw * 15625) / (__MAX17055_Resistor__ * 100) / 100000;
// End Function
return _Value;
}
uint16_t MAX17055::stateOfCharge(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x06); // RepSOC
// Calculate Measurement
uint8_t _Value = (_Measurement_Raw >> 8) & 0xFF;
// End Function
return _Value;
}
uint16_t MAX17055::averageStateOfCharge(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x0E);
// Calculate Measurement
uint16_t _Value = ((uint32_t)_Measurement_Raw * 100 / 256);
// End Function
return _Value;
}
uint16_t MAX17055::instantCapacity(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x05);
// Calculate Data
uint16_t _Value = _Measurement_Raw * 5 / 1000 / __MAX17055_Resistor__;
// End Function
return _Value;
}
uint16_t MAX17055::designCapacity(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x18);
// Calculate Data
uint16_t _Value = _Measurement_Raw * 5 / 1000 / __MAX17055_Resistor__;
// End Function
return _Value;
}
uint16_t MAX17055::fullCapacity(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x35);
// Calculate Data
uint16_t _Value = _Measurement_Raw * 5 / 1000 / __MAX17055_Resistor__;
// End Function
return _Value;
}
uint16_t MAX17055::icTemperature(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x08);
// Declare Variables
bool _Signiture = false;
// Control for Negative Value
if ((_Measurement_Raw >> 12) == 0xF) {
// Calculate Data
_Measurement_Raw = 0xFFFF - _Measurement_Raw;
// Assign Signiture
_Signiture = true;
}
// Calculate Data
uint16_t _Value = ((uint32_t)_Measurement_Raw * 100 / 256);
// Assign Signiture
if (_Signiture) _Value = -_Value;
// End Function
return _Value;
}
uint16_t MAX17055::timeToEmpty(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x11);
// Calculate Data
uint16_t _Value = ((uint32_t)_Measurement_Raw * 5625 / 60 / 60 / 100);
// End Function
return _Value;
}
uint16_t MAX17055::timeToFull(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x20);
// Calculate Data
uint16_t _Value = ((uint32_t)_Measurement_Raw * 5625 / 60 / 60 / 100);
// End Function
return _Value;
}
uint16_t MAX17055::batteryAge(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x07);
// End Function
return _Measurement_Raw;
}
uint16_t MAX17055::chargeCycle(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x17);
// End Function
return _Measurement_Raw;
}
bool MAX17055::statusControl(const uint8_t _Status) {
// Define Data Variable
uint8_t _Status_Register[2] = {0x00, 0x00};
// Read Status Register
readMultipleRegister(0x00, _Status_Register, 2, false);
// Control for Status
if (_Status == MAX17055_POR)
return bitRead(_Status_Register[0], 1);
else if (_Status == MAX17055_IMin)
return bitRead(_Status_Register[0], 2);
else if (_Status == MAX17055_IMax)
return bitRead(_Status_Register[0], 6);
else if (_Status == MAX17055_VMin)
return bitRead(_Status_Register[1], 0);
else if (_Status == MAX17055_VMax)
return bitRead(_Status_Register[1], 4);
else if (_Status == MAX17055_TMin)
return bitRead(_Status_Register[1], 1);
else if (_Status == MAX17055_TMax)
return bitRead(_Status_Register[1], 5);
else if (_Status == MAX17055_SOC_Min)
return bitRead(_Status_Register[1], 2);
else if (_Status == MAX17055_SOC_Max)
return bitRead(_Status_Register[1], 6);
else if (_Status == MAX17055_SOC_Change)
return bitRead(_Status_Register[0], 7);
else if (_Status == MAX17055_Bat_Status)
return bitRead(_Status_Register[0], 3);
else if (_Status == MAX17055_Bat_Insert)
return bitRead(_Status_Register[1], 3);
else if (_Status == MAX17055_Bat_Remove)
return bitRead(_Status_Register[1], 7);
// End Function
return false;
}
void MAX17055::statusClear(void) {
// Define Data Variable
const uint8_t _Status_Register[2] = {0x00, 0x00};
// Write Status Register
writeMultipleRegister(0x00, _Status_Register, 2);
}
uint16_t MAX17055::chargeTerminationCurrent(void) {
// Get Data from IC
uint16_t _Measurement_Raw = read_register(0x1E);
// Calculate Data
uint16_t Value = (((uint32_t)_Measurement_Raw * 1.5625) / __MAX17055_Resistor__);
// End Function
return Value;
}
} /* namespace max17055 */
} // namespace battery
+27 -13
View File
@@ -32,7 +32,6 @@
#include "baseband_packet.hpp"
#include "acars_packet.hpp"
#include "adsb_frame.hpp"
#include "ert_packet.hpp"
#include "pocsag_packet.hpp"
@@ -126,6 +125,8 @@ class Message {
BatteryStateData = 68,
ProtoViewData = 69,
FreqChangeCommand = 70,
I2CDevListChanged = 71,
LightData = 72,
MAX
};
@@ -377,13 +378,12 @@ class POCSAGStatsMessage : public Message {
class ACARSPacketMessage : public Message {
public:
constexpr ACARSPacketMessage(
const baseband::Packet& packet)
: Message{ID::ACARSPacket},
packet{packet} {
}
baseband::Packet packet;
constexpr ACARSPacketMessage()
: Message{ID::ACARSPacket} {}
uint8_t msg_len = 0;
char message[250] = {0}; // contains the whole packet
uint8_t crc[2] = {0};
uint8_t state = 0; // for debug
};
class ADSBFrameMessage : public Message {
@@ -1330,18 +1330,26 @@ class EnvironmentDataMessage : public Message {
constexpr EnvironmentDataMessage(
float temperature = 0,
float humidity = 0,
float pressure = 0,
uint16_t light = 0)
float pressure = 0)
: Message{ID::EnvironmentData},
temperature{temperature},
humidity{humidity},
pressure{pressure},
light{light} {
pressure{pressure} {
}
float temperature = 0; // celsius
float humidity = 0; // percent (rh)
float pressure = 0; // hpa
uint16_t light = 0; // lux
};
class LightDataMessage : public Message {
public:
constexpr LightDataMessage(
uint16_t light = 0)
: Message{ID::LightData},
light{light} {
}
uint16_t light = 0; // lux
};
class AudioBeepMessage : public Message {
@@ -1423,4 +1431,10 @@ class FreqChangeCommandMessage : public Message {
int64_t freq = 0;
};
class I2CDevListChangedMessage : public Message {
public:
constexpr I2CDevListChangedMessage()
: Message{ID::I2CDevListChanged} {}
};
#endif /*__MESSAGE_H__*/
+1 -1
View File
@@ -78,7 +78,7 @@ void IO::reference_oscillator(const bool enable) {
}
bool IO::get_dark_cover() {
return portapack::persistent_memory::apply_fake_brightness();
return portapack::persistent_memory::apply_fake_brightness() & (!portapack::persistent_memory::config_lcd_inverted_mode());
}
bool IO::get_is_inverted() {
+4 -16
View File
@@ -163,10 +163,7 @@ class IO {
void lcd_write_pixel(ui::Color pixel) {
if (get_dark_cover()) {
if (!get_is_inverted())
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
else
pixel.v = UNDARKENED_PIXEL(pixel.v, get_brightness());
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
}
lcd_write_data(pixel.v);
}
@@ -177,10 +174,7 @@ class IO {
void lcd_write_pixels(ui::Color pixel, size_t n) {
if (get_dark_cover()) {
if (!get_is_inverted())
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
else
pixel.v = UNDARKENED_PIXEL(pixel.v, get_brightness());
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
}
while (n--) {
lcd_write_data(pixel.v);
@@ -189,10 +183,7 @@ class IO {
void lcd_write_pixels_unrolled8(ui::Color pixel, size_t n) {
if (get_dark_cover()) {
if (!get_is_inverted())
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
else
pixel.v = UNDARKENED_PIXEL(pixel.v, get_brightness());
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
}
auto v = pixel.v;
n >>= 3;
@@ -426,10 +417,7 @@ class IO {
uint32_t original_value = (value_high << 8) | value_low;
if (get_dark_cover()) {
if (!get_is_inverted())
original_value = DARKENED_PIXEL(original_value, get_brightness());
else
original_value = UNDARKENED_PIXEL(original_value, get_brightness());
original_value = DARKENED_PIXEL(original_value, get_brightness());
}
return original_value;
}
@@ -953,18 +953,15 @@ bool ui_hide_sd_card() {
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;
}
uint8_t ui_theme_id() {
return data->ui_config2.theme_id;
}
bool ui_override_batt_calc() {
return data->ui_config2.override_batt_calc;
}
@@ -1129,7 +1126,7 @@ void set_fake_brightness_level(uint8_t v) {
// Cycle through 4 brightness options: disabled -> enabled/50% -> enabled/25% -> enabled/12.5% -> disabled
void toggle_fake_brightness_level() {
bool fbe = apply_fake_brightness();
if (config_lcd_inverted_mode()) return; // for now only inverted mode OR fake brightness
if ((!fbe) || (data->fake_brightness_level >= BRIGHTNESS_12p5)) {
set_apply_fake_brightness(!fbe);
data->fake_brightness_level = BRIGHTNESS_50;
+3 -1
View File
@@ -1,7 +1,9 @@
#include "ui_language.hpp"
const char* LanguageHelper::englishMessages[] = {"OK", "Cancel", "Error", "Modem setup", "Debug", "Log", "Done", "Start", "Stop", "Scan", "Clear", "Ready", "Data:", "Loop", "Reset"};
// use the exact position in this array! the enum's value is the identifier. Best to add to the end
const char* LanguageHelper::englishMessages[] = {"OK", "Cancel", "Error", "Modem setup", "Debug", "Log", "Done", "Start", "Stop", "Scan", "Clear", "Ready", "Data:", "Loop", "Reset", "Pause", "Resume", "Flood", "Show QR", "Save"};
// multi language support will changes (not in use for now)
const char** LanguageHelper::currentMessages = englishMessages;
void LanguageHelper::setLanguage(LanguageList lang) {
+40 -2
View File
@@ -1,10 +1,43 @@
/*
* 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.
*/
/*
The purpose of this: when creating an ext app it's address will be changed. But the compiler doesn't know this, so when it optimizes literals it can remove something from one ext app, that is included in the other, to save space.
But when we want to run this app, it won't find the literal at the fake address, so the best case we'll get just garbage or nothing instead of the string.
So we must use this Language helper that stores the literals in the fw instead of any ext app, so it'll be always available.
In the first run we must move those string literal here what are not so unique, like "OK".
*/
#ifndef __UI_LANGUAGE_H__
#define __UI_LANGUAGE_H__
// For future multi language support
enum LanguageList {
ENGLISH,
};
// Add an element to this enum when you fill a new value.
// Then add the string literal to the LanguageHelper::englishMessages at the exact position where the enum points to (usualy to the end)
enum LangConsts {
LANG_OK,
LANG_CANCEL,
@@ -20,12 +53,17 @@ enum LangConsts {
LANG_READY,
LANG_DATADP,
LANG_LOOP,
LANG_RESET
LANG_RESET,
LANG_PAUSE,
LANG_RESUME,
LANG_FLOOD,
LANG_SHOWQR,
LANG_SAVE
};
class LanguageHelper {
public:
static void setLanguage(LanguageList lang);
static void setLanguage(LanguageList lang); // changes the pointer to the currently used language literal container. not used yet.
static const char* getMessage(LangConsts msg);
static const char** currentMessages; // expose, so can link directly too
+25 -8
View File
@@ -33,6 +33,7 @@
#include "string_format.hpp"
#include "usb_serial_device_to_host.h"
#include "rtc_time.hpp"
#include "battery.hpp"
using namespace portapack;
using namespace rtc_time;
@@ -971,7 +972,7 @@ void Button::paint(Painter& painter) {
const Style paint_style = {style().font, bg, fg};
painter.draw_rectangle({r.location(), {r.size().width(), 1}}, Theme::getInstance()->fg_light->foreground);
painter.draw_rectangle({r.location(), {r.size().width(), 1}}, Theme::getInstance()->bg_light->background);
painter.draw_rectangle({r.location().x(), r.location().y() + r.size().height() - 1, r.size().width(), 1}, Theme::getInstance()->bg_dark->background);
painter.draw_rectangle({r.location().x() + r.size().width() - 1, r.location().y(), 1, r.size().height()}, Theme::getInstance()->bg_dark->background);
@@ -1311,7 +1312,7 @@ void NewButton::paint(Painter& painter) {
const auto r = screen_rect();
const Style style = paint_style();
painter.draw_rectangle({r.location(), {r.width(), 1}}, Theme::getInstance()->fg_light->foreground);
painter.draw_rectangle({r.location(), {r.width(), 1}}, Theme::getInstance()->bg_light->background);
painter.draw_rectangle({r.left(), r.top() + r.height() - 1, r.width(), 1}, Theme::getInstance()->bg_dark->background);
painter.draw_rectangle({r.left() + r.width() - 1, r.top(), 1, r.height()}, Theme::getInstance()->bg_dark->background);
@@ -1697,10 +1698,12 @@ bool ImageOptionsField::on_touch(const TouchEvent event) {
OptionsField::OptionsField(
Point parent_pos,
size_t length,
options_t options)
options_t options,
bool centered)
: Widget{{parent_pos, {8 * (int)length, 16}}},
length_{length},
options_{std::move(options)} {
options_{std::move(options)},
centered_{centered} {
set_focusable(true);
}
@@ -1792,8 +1795,19 @@ void OptionsField::paint(Painter& painter) {
std::string_view temp = selected_index_name();
if (temp.length() > length_)
temp = temp.substr(0, length_);
Point draw_pos = screen_pos();
if (centered_) {
// 8 is because big font width is 8px
// type is from: struct Point : constexpr int x() const
int text_width = temp.length() * 8;
int available_width = length_ * 8;
int x_offset = (available_width - text_width) / 2;
draw_pos = {draw_pos.x() + x_offset, draw_pos.y()};
}
painter.draw_string(
screen_pos(),
draw_pos,
paint_style,
temp);
}
@@ -2622,6 +2636,8 @@ void Waveform::set_length(const uint32_t new_length) {
}
void Waveform::paint(Painter& painter) {
// previously it's upside down , low level is up and high level is down, which doesn't make sense,
// if that was made for a reason, feel free to revert.
size_t n;
Coord y, y_offset = screen_rect().location().y();
Coord prev_x = screen_rect().location().x(), prev_y;
@@ -2642,7 +2658,7 @@ void Waveform::paint(Painter& painter) {
x = 0;
h--;
for (n = 0; n < length_; n++) {
y = *(data_start++) ? h : 0;
y = *(data_start++) ? 0 : h;
if (n) {
if (y != prev_y)
@@ -2658,9 +2674,10 @@ void Waveform::paint(Painter& painter) {
// Analog waveform: each value is a point's Y coordinate
x = prev_x + x_inc;
h /= 2;
prev_y = y_offset + h - (*(data_start++) * y_scale);
prev_y = y_offset + h + (*(data_start++) * y_scale);
for (n = 1; n < length_; n++) {
y = y_offset + h - (*(data_start++) * y_scale);
y = y_offset + h + (*(data_start++) * y_scale);
display.draw_line({prev_x, prev_y}, {(Coord)x, y}, color_);
prev_x = x;
+5 -2
View File
@@ -156,6 +156,8 @@ class Widget {
};
class View : public Widget {
// unlike Paint class, our Y ignored the top bar;
// so when you draw some of us as Y = 0, it would be exact below the top bar, instead of overlapped with top bar
public:
View() {
}
@@ -678,7 +680,7 @@ class OptionsField : public Widget {
std::function<void(size_t, value_t)> on_change{};
std::function<void(void)> on_show_options{};
OptionsField(Point parent_pos, size_t length, options_t options);
OptionsField(Point parent_pos, size_t length, options_t options, bool centered = false);
options_t& options() { return options_; }
const options_t& options() const { return options_; }
@@ -706,6 +708,7 @@ class OptionsField : public Widget {
const size_t length_;
options_t options_;
size_t selected_index_{0};
bool centered_{false}; // e.g.: length as screen_width/8, x position as 0, it will be centered in x axis
};
// A TextEdit is bound to a string reference and allows the string
@@ -1034,4 +1037,4 @@ class OptionTabView : public View {
} /* namespace ui */
#endif /*__UI_WIDGET_H__*/
#endif /*__UI_WIDGET_H__*/
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

+1
View File
@@ -0,0 +1 @@
bitmap is for icons, it's colorless and headless; splash and modal isn't using bitmap
+1
View File
@@ -0,0 +1 @@
bmp is for splash and modal warning, it contains color info and file header; icons isn't using bmp.
@@ -0,0 +1,49 @@
# copyleft zxkmm 2024
import os
import sys
def bmp_to_hex_cpp_arr(input_file, output_file):
with open(input_file, 'rb') as f:
data = f.read()
name_without_extension = os.path.splitext(os.path.basename(input_file))[0]
with open(output_file, 'w') as f:
f.write(f"// converted by bmp_to_hex_cpp_arr.py at the firmware/tools dir\n")
f.write(f"// fake transparent px should be the exact color rgb(255,0,255), then use true for the last arg "
f"when drawing bmp with the draw func\n")
f.write(f"\n")
f.write(f"const unsigned char {name_without_extension}[] = {{\n")
# idk about the clang-format rule, but the splash arr is 12 item per line. but other bmp arr is 2 item per line
for i in range(0, len(data), 12):
line = data[i:i + 12]
hex_values = [f"0x{byte:02x}" for byte in line]
f.write(f" {', '.join(hex_values)},\n")
# this is for remove last the extra comma
f.seek(f.tell() - 2, os.SEEK_SET)
f.truncate()
f.write("};\n")
f.write(f"unsigned int {name_without_extension}_len = {len(data)};\n")
print(f"done, outputted, pls clang-format yourself before commit (if necessary){output_file}")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("usage: python bmp_to_hex_cpp_arr.py INPUTFILE OUTPUTFILE")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
bmp_to_hex_cpp_arr(input_file, output_file)
@@ -0,0 +1,51 @@
# copyleft zxkmm 2024
import sys
import re
def hex_cpp_arr_to_bmp(input_file, output_file):
with open(input_file, 'r') as f:
content = f.read()
# in the mayhem code some of them not const
array_name_const = re.search(r'const unsigned char (\w+)\[\]', content)
array_name_no_const = re.search(r'unsigned char (\w+)\[]', content)
if array_name_const or array_name_no_const:
if array_name_const:
array_name = array_name_const.group(1)
else:
array_name = array_name_no_const.group(1)
print(f"Array name: {array_name}")
else:
print("the file provided seems doesn't contains needed arr.")
return
hex_data = re.findall(r'0x[0-9A-Fa-f]{2}', content)
if not hex_data:
print("Error: No hex data found.")
return
binary_data = bytes([int(x, 16) for x in hex_data])
with open(output_file, 'wb') as f:
f.write(binary_data)
print(f"Done. Output file: {output_file}")
print(f"Array name: {array_name}")
print(f"Data length: {len(binary_data)} bytes")
if __name__ == "__main__":
if len(sys.argv) != 3:
print("usage: python hex_cpp_arr_to_bmp.py INPUTFILE OUTPUTFILE")
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
hex_cpp_arr_to_bmp(input_file, output_file)
Binary file not shown.

After

Width:  |  Height:  |  Size: 85 B