mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-21 15:09:03 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 09c2c43be0 | |||
| b9771b2350 | |||
| 83b65ba6ce | |||
| d4edb5f5f9 | |||
| 536d25db64 | |||
| eca1db26e4 | |||
| 7d96bc4efe | |||
| b0c92d9baf | |||
| c1af935022 | |||
| e75fb59169 | |||
| 2d1314a3fa | |||
| a398ed1634 | |||
| 105742acbc | |||
| b43eaa8786 | |||
| b19ed5fec5 | |||
| 419bc75d2f | |||
| b3a0ad018c | |||
| a21e9ca1af | |||
| 20f45e88f3 | |||
| 967506fb97 | |||
| 24ab2f29fa |
+20
-7
@@ -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)
|
||||
|
||||
@@ -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="It’s 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="It’s 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:  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:  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:
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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__*/
|
||||
@@ -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>(); }});
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ typedef enum {
|
||||
CT_MAX283X,
|
||||
CT_SI5351,
|
||||
CT_AUDIO,
|
||||
CT_BATTERY,
|
||||
CT_MAX17055,
|
||||
} chip_type_t;
|
||||
|
||||
struct RegistersWidgetConfig {
|
||||
|
||||
+15
-8
@@ -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));
|
||||
}
|
||||
|
||||
+3
-2
@@ -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);
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 )
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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},
|
||||
|
||||
Vendored
+34
-47
@@ -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
|
||||
Vendored
+12
-14
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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__*/
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
@@ -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
|
||||
@@ -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__*/
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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},
|
||||
""};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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__*/
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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__*/
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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__ */
|
||||
@@ -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 */
|
||||
@@ -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__ */
|
||||
@@ -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(®, 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(®, 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
|
||||
@@ -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__ */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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(®, 1, &data, 1);
|
||||
}
|
||||
|
||||
uint8_t I2cDev::read8_1(uint8_t reg) {
|
||||
uint8_t res = 0;
|
||||
i2c_read(®, 1, &res, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
uint16_t I2cDev::read16_1(uint8_t reg) {
|
||||
uint8_t buffer[2];
|
||||
i2c_read(®, 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(®, 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
|
||||
@@ -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
|
||||
@@ -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++];
|
||||
|
||||
@@ -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
@@ -32,7 +32,6 @@
|
||||
|
||||
#include "baseband_packet.hpp"
|
||||
|
||||
#include "acars_packet.hpp"
|
||||
#include "adsb_frame.hpp"
|
||||
#include "ert_packet.hpp"
|
||||
#include "pocsag_packet.hpp"
|
||||
@@ -126,6 +125,8 @@ class Message {
|
||||
BatteryStateData = 68,
|
||||
ProtoViewData = 69,
|
||||
FreqChangeCommand = 70,
|
||||
I2CDevListChanged = 71,
|
||||
LightData = 72,
|
||||
MAX
|
||||
};
|
||||
|
||||
@@ -377,13 +378,12 @@ class POCSAGStatsMessage : public Message {
|
||||
|
||||
class ACARSPacketMessage : public Message {
|
||||
public:
|
||||
constexpr ACARSPacketMessage(
|
||||
const baseband::Packet& packet)
|
||||
: Message{ID::ACARSPacket},
|
||||
packet{packet} {
|
||||
}
|
||||
|
||||
baseband::Packet packet;
|
||||
constexpr ACARSPacketMessage()
|
||||
: Message{ID::ACARSPacket} {}
|
||||
uint8_t msg_len = 0;
|
||||
char message[250] = {0}; // contains the whole packet
|
||||
uint8_t crc[2] = {0};
|
||||
uint8_t state = 0; // for debug
|
||||
};
|
||||
|
||||
class ADSBFrameMessage : public Message {
|
||||
@@ -1330,18 +1330,26 @@ class EnvironmentDataMessage : public Message {
|
||||
constexpr EnvironmentDataMessage(
|
||||
float temperature = 0,
|
||||
float humidity = 0,
|
||||
float pressure = 0,
|
||||
uint16_t light = 0)
|
||||
float pressure = 0)
|
||||
: Message{ID::EnvironmentData},
|
||||
temperature{temperature},
|
||||
humidity{humidity},
|
||||
pressure{pressure},
|
||||
light{light} {
|
||||
pressure{pressure} {
|
||||
}
|
||||
float temperature = 0; // celsius
|
||||
float humidity = 0; // percent (rh)
|
||||
float pressure = 0; // hpa
|
||||
uint16_t light = 0; // lux
|
||||
};
|
||||
|
||||
class LightDataMessage : public Message {
|
||||
public:
|
||||
constexpr LightDataMessage(
|
||||
|
||||
uint16_t light = 0)
|
||||
: Message{ID::LightData},
|
||||
light{light} {
|
||||
}
|
||||
uint16_t light = 0; // lux
|
||||
};
|
||||
|
||||
class AudioBeepMessage : public Message {
|
||||
@@ -1423,4 +1431,10 @@ class FreqChangeCommandMessage : public Message {
|
||||
int64_t freq = 0;
|
||||
};
|
||||
|
||||
class I2CDevListChangedMessage : public Message {
|
||||
public:
|
||||
constexpr I2CDevListChangedMessage()
|
||||
: Message{ID::I2CDevListChanged} {}
|
||||
};
|
||||
|
||||
#endif /*__MESSAGE_H__*/
|
||||
|
||||
@@ -1,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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 |
Reference in New Issue
Block a user