Compare commits

..

21 Commits

Author SHA1 Message Date
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
70 changed files with 3027 additions and 790 deletions
+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: ![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. Join their giveaways on discord (check the badge on top).
: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: 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: 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: 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: 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:
+6 -5
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
@@ -266,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
@@ -292,7 +293,7 @@ set(CPPSRC
apps/ui_btle_rx.cpp
# apps/ui_coasterp.cpp
apps/ui_debug.cpp
apps/ui_debug_battery.cpp
apps/ui_debug_max17055.cpp
apps/ui_dfu_menu.cpp
apps/ui_encoders.cpp
apps/ui_fileman.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;
}
}
+2 -1
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.
*
@@ -185,4 +186,4 @@ BattinfoView::~BattinfoView() {
thread = nullptr;
}
}
} // namespace ui
} // namespace ui
+3 -1
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 {
@@ -104,4 +106,4 @@ class BattinfoView : public View {
} /* namespace ui */
#endif /*__UI_BATTINFO__*/
#endif /*__UI_BATTINFO__*/
+12 -8
View File
@@ -36,7 +36,7 @@
#include "ui_font_fixed_8x16.hpp"
#include "ui_painter.hpp"
#include "ui_external_items_menu_loader.hpp"
#include "ui_debug_battery.hpp"
#include "ui_debug_max17055.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
@@ -226,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);
}
@@ -249,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;
@@ -465,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
}
@@ -512,7 +516,7 @@ void DebugMenuView::on_populate() {
{"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
});
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BatteryModules::BATT_MAX17055) {
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>(); }});
}
+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 {
@@ -1,11 +1,11 @@
#include "ui_debug_battery.hpp"
#include "ui_debug_max17055.hpp"
#include "string_format.hpp"
namespace ui {
BatteryCapacityView::RegisterEntry BatteryCapacityView::get_entry(size_t index) {
if (index < battery::max17055::MAX17055::entries_count) {
return battery::max17055::MAX17055::entries[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};
}
@@ -27,6 +27,12 @@ BatteryCapacityView::BatteryCapacityView(NavigationView& nav) {
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();
}
@@ -37,7 +43,7 @@ void BatteryCapacityView::focus() {
bool BatteryCapacityView::on_encoder(const EncoderEvent delta) {
int32_t new_page = current_page + delta;
if (new_page >= 0 && new_page < ((int32_t)battery::max17055::MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE) {
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();
@@ -46,11 +52,12 @@ bool BatteryCapacityView::on_encoder(const EncoderEvent delta) {
}
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 < battery::max17055::MAX17055::entries_count) {
if (entry_index < i2cdev::I2cDev_MAX17055::entries_count) {
const auto entry = get_entry(entry_index);
uint16_t raw_value = battery::BatteryManagement::read_register(entry.address);
uint16_t raw_value = dev->read_register(entry.address);
hex_texts[i].set("0x" + to_string_hex(raw_value, 4));
@@ -78,7 +85,7 @@ void BatteryCapacityView::update_values() {
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 < battery::max17055::MAX17055::entries_count) {
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));
@@ -97,7 +104,7 @@ void BatteryCapacityView::populate_page(int start_index) {
}
void BatteryCapacityView::update_page_text() {
int total_pages = (battery::max17055::MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE;
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));
}
@@ -5,7 +5,8 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "battery.hpp"
#include "max17055.hpp"
#include "i2cdevmanager.hpp"
#include "i2cdev_max17055.hpp"
namespace ui {
@@ -17,7 +18,7 @@ class BatteryCapacityView : public View {
bool on_encoder(const EncoderEvent delta) override;
using RegisterEntry = battery::max17055::RegisterEntry;
using RegisterEntry = i2cdev::I2cDev_MAX17055::RegisterEntry;
private:
static RegisterEntry get_entry(size_t index);
+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();
};
@@ -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 )
+13 -2
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;
@@ -876,6 +879,7 @@ SetAutostartView::SetAutostartView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&button_reset,
&options});
button_save.on_select = [&nav, this](Button&) {
@@ -892,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};
@@ -969,7 +979,7 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
&button_cancel,
&checkbox_overridebatt});
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BATT_MAX17055) add_children({&button_reset, &labels2});
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());
@@ -979,7 +989,8 @@ SetBatteryView::SetBatteryView(NavigationView& nav) {
};
button_reset.on_select = [&nav, this](Button&) {
if (battery::BatteryManagement::reset_learned())
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");
+12 -6
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},
@@ -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
};
}
+13
View File
@@ -96,6 +96,17 @@ set(EXTCPPSRC
#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
@@ -122,4 +133,6 @@ set(EXTAPPLIST
adsbtx
morse_tx
sstvtx
random_password
#acars_rx
)
+15
View File
@@ -46,6 +46,8 @@ MEMORY
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
@@ -190,6 +192,19 @@ SECTIONS
*(*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
+41 -15
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,10 +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,
@@ -72,6 +73,10 @@ ProtoView::ProtoView(NavigationView& nav)
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();
@@ -80,6 +85,7 @@ 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;
@@ -134,19 +140,20 @@ void ProtoView::draw() {
drawcnt = 0;
for (uint16_t i = 0; i < MAXDRAWCNT; i++) waveform_buffer[i] = 0; // reset
// add empty data for padding (so you can shift left/nagetive)
for (int32_t i = 0;
i < ((waveform_shift > 0) ? 0 : -waveform_shift) && drawcnt < MAXDRAWCNT; // this is for shift nagetive (left side)
++i) {
waveform_buffer[drawcnt++] = 0;
// 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;
}
}
for (uint16_t i = ((waveform_shift < 0) ? -waveform_shift : 0); // this is for shift positive aka right side
i < MAXSIGNALBUFFER && drawcnt < MAXDRAWCNT; // prevent out of ranging
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) {
uint16_t buffer_index = (i + waveform_shift + MAXSIGNALBUFFER) % MAXSIGNALBUFFER;
state = time_buffer[buffer_index] >= 0;
int32_t timeabs = state ? time_buffer[buffer_index] : -1 * time_buffer[buffer_index];
state = time_buffer[i] >= 0;
int32_t timeabs = state ? time_buffer[i] : -1 * time_buffer[i];
int32_t timesize = timeabs / zoom;
if (timesize == 0) {
remain += timeabs;
@@ -164,7 +171,11 @@ void ProtoView::draw() {
remain = 0;
lmax = 0;
for (int32_t ii = 0; ii < timesize && drawcnt < MAXDRAWCNT; ++ii) {
waveform_buffer[drawcnt++] = state;
if (skipped < to_skip) {
skipped++;
} else {
waveform_buffer[drawcnt++] = state;
}
}
}
}
@@ -175,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;
@@ -207,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();
+12 -3
View File
@@ -74,8 +74,11 @@ class ProtoView : public View {
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
nav_};
Labels labels{
{{0 * 8, 1 * 16}, "Zoom: ", Theme::getInstance()->fg_light->foreground},
// 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{
@@ -103,6 +106,10 @@ class ProtoView : public View {
{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, 8 * 8, 240, 50},
waveform_buffer,
@@ -136,6 +143,7 @@ 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;
@@ -151,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{
@@ -178,4 +187,4 @@ class ProtoView : public View {
} // namespace ui::external_app::protoview
#endif /*__UI_PROTOVIEW_H__*/
#endif /*__UI_PROTOVIEW_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_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__*/
+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
+66 -25
View File
@@ -86,7 +86,6 @@
#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
@@ -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>()},
@@ -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);
@@ -416,14 +413,11 @@ void SystemStatusView::refresh() {
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);
}
}
@@ -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, true);
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},
+9 -6
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
@@ -579,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
+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 -163
View File
@@ -1,151 +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_max17055.detect()) {
battery_max17055.init();
detected_ = BATT_MAX17055;
return;
}
if (battery_ads1110.detect()) {
battery_ads1110.init();
detected_ = BATT_ADS1110;
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();
}
bool BatteryManagement::reset_learned() {
if (detected_ == BATT_MAX17055) {
return battery_max17055.reset_learned();
}
return false;
}
// 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::get_cycles() {
if (detected_ == BATT_MAX17055) {
return (uint16_t)battery_max17055.getValue("Cycles");
}
return 0;
}
float BatteryManagement::get_tte() {
if (detected_ == BATT_MAX17055) {
return battery_max17055.getValue("TTE");
}
return 0;
}
float BatteryManagement::get_ttf() {
if (detected_ == BATT_MAX17055) {
return battery_max17055.getValue("TTF");
}
return 0;
}
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;
@@ -154,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
+4 -22
View File
@@ -23,7 +23,6 @@
#define __BATTERY_H__
#include <cstdint>
#include "ch.h"
namespace battery {
@@ -31,14 +30,9 @@ 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,
@@ -47,28 +41,16 @@ class BatteryManagement {
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 reset_learned(); // resets the ic's learned parameters
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
@@ -19,17 +19,17 @@ 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 "i2cdev_max17055.hpp"
#include "battery.hpp"
#include "utility.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>
#include <algorithm>
#include <cstdint>
namespace battery {
namespace max17055 {
namespace i2cdev {
const RegisterEntry MAX17055::entries[] = {
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},
@@ -184,11 +184,24 @@ const RegisterEntry MAX17055::entries[] = {
{"Reserved", 0xFF, "", 0.00390625, false, "%", true, 6, true, false, false, 0, true},
};
void MAX17055::init() {
if (!detected_) {
detected_ = detect();
}
if (detected_) {
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();
@@ -198,10 +211,12 @@ void MAX17055::init() {
}
partialInit();
// statusClear(); I am not sure if this should be here or not
return true;
}
return false;
}
bool MAX17055::full_reset_and_init() {
bool I2cDev_MAX17055::full_reset_and_init() {
if (!soft_reset()) {
return false;
}
@@ -221,11 +236,11 @@ bool MAX17055::full_reset_and_init() {
return true;
}
bool MAX17055::soft_reset() {
bool I2cDev_MAX17055::soft_reset() {
return write_register(0x0BB, 0x0000);
}
bool MAX17055::initialize_custom_parameters() {
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
@@ -241,7 +256,7 @@ bool MAX17055::initialize_custom_parameters() {
return true;
}
bool MAX17055::load_custom_parameters() {
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
@@ -259,13 +274,13 @@ bool MAX17055::load_custom_parameters() {
return true;
}
bool MAX17055::clear_por() {
bool I2cDev_MAX17055::clear_por() {
uint16_t status = read_register(0x00);
status &= ~(1 << 1);
return write_register(0x00, status);
}
bool MAX17055::needsInitialization() {
bool I2cDev_MAX17055::needsInitialization() {
uint16_t UserMem1 = read_register(0x40);
if (UserMem1 == 0) {
@@ -274,41 +289,38 @@ bool MAX17055::needsInitialization() {
return false;
}
void MAX17055::partialInit() {
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 MAX17055::reset_learned() {
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 MAX17055::detect() {
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) {
detected_ = true;
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) {
detected_ = true;
return true;
}
detected_ = false;
return false;
}
const RegisterEntry* MAX17055::findEntry(const char* name) const {
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;
@@ -317,48 +329,47 @@ const RegisterEntry* MAX17055::findEntry(const char* name) const {
return nullptr;
}
uint16_t MAX17055::read_register(const uint8_t reg) {
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};
bus.transmit(bus_address, tx.data(), tx.size());
bus.receive(bus_address, rx.data(), rx.size());
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 MAX17055::write_register(const uint8_t reg, const uint16_t value) {
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 = bus.transmit(bus_address, tx.data(), tx.size());
bool success = i2cbus.transmit(addr, tx.data(), tx.size());
chThdSleepMilliseconds(1);
return success;
}
void MAX17055::getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current) {
if (detected_) {
// 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
} else {
// let's indicate the data is wrong. ui will handle this by display UNK values.
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 MAX17055::getValue(const char* entityName) {
float I2cDev_MAX17055::getValue(const char* entityName) {
const RegisterEntry* entry = findEntry(entityName);
if (entry) {
uint16_t raw_value = read_register(entry->address);
@@ -376,11 +387,11 @@ float MAX17055::getValue(const char* entityName) {
return 0; // Return 0 if entry not found
}
uint16_t MAX17055::averageMVoltage(void) {
uint16_t I2cDev_MAX17055::averageMVoltage(void) {
return static_cast<uint16_t>(getValue("AvgVCell") * 1000.0f); // Convert to millivolts
}
int32_t MAX17055::instantCurrent(void) {
int32_t I2cDev_MAX17055::instantCurrent(void) {
return getValue("Current");
// Get Data from IC
@@ -395,11 +406,11 @@ int32_t MAX17055::instantCurrent(void) {
return _Value;
}
uint16_t MAX17055::stateOfCharge(void) {
uint16_t I2cDev_MAX17055::stateOfCharge(void) {
return getValue("RepSOC");
}
bool MAX17055::setEmptyVoltage(uint16_t _Empty_Voltage) {
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;
@@ -413,7 +424,7 @@ bool MAX17055::setEmptyVoltage(uint16_t _Empty_Voltage) {
return write_register(0x3A, new_value);
}
bool MAX17055::setRecoveryVoltage(uint16_t _Recovery_Voltage) {
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
@@ -427,7 +438,7 @@ bool MAX17055::setRecoveryVoltage(uint16_t _Recovery_Voltage) {
return write_register(0x3A, new_value);
}
bool MAX17055::setMinVoltage(uint16_t _Minimum_Voltage) {
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
@@ -436,7 +447,7 @@ bool MAX17055::setMinVoltage(uint16_t _Minimum_Voltage) {
return write_register(0x01, new_value);
}
bool MAX17055::setMaxVoltage(uint16_t _Maximum_Voltage) {
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
@@ -445,7 +456,7 @@ bool MAX17055::setMaxVoltage(uint16_t _Maximum_Voltage) {
return write_register(0x01, new_value);
}
bool MAX17055::setMaxCurrent(uint16_t _Maximum_Current) {
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
@@ -454,34 +465,34 @@ bool MAX17055::setMaxCurrent(uint16_t _Maximum_Current) {
return write_register(0xB4, new_value);
}
bool MAX17055::setChargeTerminationCurrent(uint16_t _Charge_Termination_Current) {
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 MAX17055::setDesignCapacity(const uint16_t _Capacity) {
bool I2cDev_MAX17055::setDesignCapacity(const uint16_t _Capacity) {
uint16_t raw_cap = (uint16_t)_Capacity * 2;
return write_register(0x18, raw_cap);
}
bool MAX17055::setFullCapRep(const uint16_t _Capacity) {
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 MAX17055::setFullCapNom(const uint16_t _Capacity) {
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 MAX17055::setRepCap(const uint16_t _Capacity) {
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 MAX17055::setMinSOC(uint8_t _Minimum_SOC) {
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;
@@ -490,7 +501,7 @@ bool MAX17055::setMinSOC(uint8_t _Minimum_SOC) {
return write_register(0x03, new_value);
}
bool MAX17055::setMaxSOC(uint8_t _Maximum_SOC) {
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;
@@ -499,7 +510,7 @@ bool MAX17055::setMaxSOC(uint8_t _Maximum_SOC) {
return write_register(0x03, new_value);
}
bool MAX17055::setMinTemperature(uint8_t _Minimum_Temperature) {
bool I2cDev_MAX17055::setMinTemperature(uint8_t _Minimum_Temperature) {
uint16_t current_value = read_register(0x02);
uint16_t min_temp_raw = (uint8_t)_Minimum_Temperature;
@@ -508,7 +519,7 @@ bool MAX17055::setMinTemperature(uint8_t _Minimum_Temperature) {
return write_register(0x02, new_value);
}
bool MAX17055::setMaxTemperature(uint8_t _Maximum_Temperature) {
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;
@@ -517,7 +528,7 @@ bool MAX17055::setMaxTemperature(uint8_t _Maximum_Temperature) {
return write_register(0x02, new_value);
}
bool MAX17055::setModelCfg(const uint8_t _Model_ID) {
bool I2cDev_MAX17055::setModelCfg(const uint8_t _Model_ID) {
uint16_t model_cfg = 0x0400; // Set Charge Voltage (bit 10)
// Set Battery Model
@@ -537,11 +548,11 @@ bool MAX17055::setModelCfg(const uint8_t _Model_ID) {
return write_register(0xDB, model_cfg);
}
bool MAX17055::setHibCFG(const uint16_t _Config) {
bool I2cDev_MAX17055::setHibCFG(const uint16_t _Config) {
return write_register(0xBA, _Config);
}
void MAX17055::config(void) {
void I2cDev_MAX17055::config(void) {
uint16_t config1 = 0x0000;
uint16_t config2 = 0x0618; // Default value: 0b00011000 00000110
@@ -575,7 +586,7 @@ void MAX17055::config(void) {
write_register(0xBB, config2);
}
bool MAX17055::statusClear() {
bool I2cDev_MAX17055::statusClear() {
// Clear all bits in the Status register (0x00)
return write_register(0x00, 0x0000);
}
@@ -584,7 +595,7 @@ bool bitRead(uint8_t value, uint8_t bit) {
return (value >> bit) & 0x01;
}
bool MAX17055::statusControl(const uint8_t _Status) {
bool I2cDev_MAX17055::statusControl(const uint8_t _Status) {
// Read Status Register (0x00)
uint16_t status_register = read_register(0x00);
@@ -621,5 +632,4 @@ bool MAX17055::statusControl(const uint8_t _Status) {
}
}
} /* namespace max17055 */
} // namespace battery
} /* 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
@@ -248,41 +248,36 @@
#define MAX17055_AtRateEn 0
#endif
namespace battery {
namespace max17055 {
namespace i2cdev {
using address_t = uint8_t;
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;
};
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;
uint16_t read_register(const uint8_t reg);
bool write_register(const uint8_t reg, const uint16_t value);
void init();
bool init(uint8_t addr_) override;
void update() override;
bool detect();
bool isDetected() const { return detected_; }
void getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current);
bool reset_learned();
@@ -293,10 +288,6 @@ class MAX17055 {
uint16_t stateOfCharge(void);
private:
I2C& bus;
const I2C::address_t bus_address;
bool detected_ = false;
const RegisterEntry* findEntry(const char* name) const;
bool needsInitialization();
@@ -330,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
+19 -5
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;
@@ -362,12 +375,13 @@ void ILI9341::drawBMP(const ui::Point p, const uint8_t* bitmap, const bool trans
// 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 == 0x29) &&
(bmp_palette->color[c].G == 0x18) &&
(bmp_palette->color[c].B == 0x16)) 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);
+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__*/
+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
+23 -6
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;
@@ -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