Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 775156bbbe | |||
| 7161a065ea | |||
|
|
21b382d728 | ||
| ccdc5e151e | |||
|
|
9ab4792354 | ||
| 99b1896c8d | |||
|
|
2f627af12d | ||
|
|
21a32e1414 |
167
.gitignore
vendored
167
.gitignore
vendored
@ -1,167 +0,0 @@
|
|||||||
|
|
||||||
questions/*
|
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
|
||||||
__pycache__/
|
|
||||||
*.py[cod]
|
|
||||||
*$py.class
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Distribution / packaging
|
|
||||||
.Python
|
|
||||||
build/
|
|
||||||
develop-eggs/
|
|
||||||
dist/
|
|
||||||
downloads/
|
|
||||||
eggs/
|
|
||||||
.eggs/
|
|
||||||
lib/
|
|
||||||
lib64/
|
|
||||||
parts/
|
|
||||||
sdist/
|
|
||||||
var/
|
|
||||||
wheels/
|
|
||||||
share/python-wheels/
|
|
||||||
*.egg-info/
|
|
||||||
.installed.cfg
|
|
||||||
*.egg
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
# PyInstaller
|
|
||||||
# Usually these files are written by a python script from a template
|
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
||||||
*.manifest
|
|
||||||
*.spec
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
pip-delete-this-directory.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
htmlcov/
|
|
||||||
.tox/
|
|
||||||
.nox/
|
|
||||||
.coverage
|
|
||||||
.coverage.*
|
|
||||||
.cache
|
|
||||||
nosetests.xml
|
|
||||||
coverage.xml
|
|
||||||
*.cover
|
|
||||||
*.py,cover
|
|
||||||
.hypothesis/
|
|
||||||
.pytest_cache/
|
|
||||||
cover/
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
*.pot
|
|
||||||
|
|
||||||
# Django stuff:
|
|
||||||
*.log
|
|
||||||
local_settings.py
|
|
||||||
db.sqlite3
|
|
||||||
db.sqlite3-journal
|
|
||||||
|
|
||||||
# Flask stuff:
|
|
||||||
instance/
|
|
||||||
.webassets-cache
|
|
||||||
|
|
||||||
# Scrapy stuff:
|
|
||||||
.scrapy
|
|
||||||
|
|
||||||
# Sphinx documentation
|
|
||||||
docs/_build/
|
|
||||||
|
|
||||||
# PyBuilder
|
|
||||||
.pybuilder/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# Jupyter Notebook
|
|
||||||
.ipynb_checkpoints
|
|
||||||
|
|
||||||
# IPython
|
|
||||||
profile_default/
|
|
||||||
ipython_config.py
|
|
||||||
|
|
||||||
# pyenv
|
|
||||||
# For a library or package, you might want to ignore these files since the code is
|
|
||||||
# intended to run in multiple environments; otherwise, check them in:
|
|
||||||
# .python-version
|
|
||||||
|
|
||||||
# pipenv
|
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
||||||
# install all needed dependencies.
|
|
||||||
#Pipfile.lock
|
|
||||||
|
|
||||||
# poetry
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
||||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
||||||
# commonly ignored for libraries.
|
|
||||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
||||||
#poetry.lock
|
|
||||||
|
|
||||||
# pdm
|
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
||||||
#pdm.lock
|
|
||||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
||||||
# in version control.
|
|
||||||
# https://pdm.fming.dev/#use-with-ide
|
|
||||||
.pdm.toml
|
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
||||||
__pypackages__/
|
|
||||||
|
|
||||||
# Celery stuff
|
|
||||||
celerybeat-schedule
|
|
||||||
celerybeat.pid
|
|
||||||
|
|
||||||
# SageMath parsed files
|
|
||||||
*.sage.py
|
|
||||||
|
|
||||||
# Environments
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
env/
|
|
||||||
venv/
|
|
||||||
venv_lin/*
|
|
||||||
ENV/
|
|
||||||
env.bak/
|
|
||||||
venv.bak/
|
|
||||||
|
|
||||||
# Spyder project settings
|
|
||||||
.spyderproject
|
|
||||||
.spyproject
|
|
||||||
|
|
||||||
# Rope project settings
|
|
||||||
.ropeproject
|
|
||||||
|
|
||||||
# mkdocs documentation
|
|
||||||
/site
|
|
||||||
|
|
||||||
# mypy
|
|
||||||
.mypy_cache/
|
|
||||||
.dmypy.json
|
|
||||||
dmypy.json
|
|
||||||
|
|
||||||
# Pyre type checker
|
|
||||||
.pyre/
|
|
||||||
|
|
||||||
# pytype static type analyzer
|
|
||||||
.pytype/
|
|
||||||
|
|
||||||
# Cython debug symbols
|
|
||||||
cython_debug/
|
|
||||||
|
|
||||||
# PyCharm
|
|
||||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
||||||
#.idea/
|
|
||||||
config.ini
|
|
||||||
logs/*
|
|
||||||
..env.swp
|
|
||||||
51
README.md
51
README.md
@ -1,39 +1,40 @@
|
|||||||
<div align="center" markdown="1">
|
# Meshtastic MOD
|
||||||
|
|
||||||
<img src=".github/meshtastic_logo.png" alt="Meshtastic Logo" width="80"/>
|
Модификация оригинальной прошивки для meshtastic. Был взят исходный код версии "2.7.15.567b8ea" (Последний стабильный релиз на момент модификации 10.12.25).
|
||||||
<h1>Meshtastic Firmware</h1>
|
|
||||||
|
|
||||||

|
В данном моде был добавлен новый регион "NO_LIMITS", у которого убраны все возможные ограничения (Frequency Range, Duty Cycle, Power Limit)
|
||||||
[](https://github.com/meshtastic/firmware/actions/workflows/ci.yml)
|
|
||||||
[](https://cla-assistant.io/meshtastic/firmware)
|
|
||||||
[](https://opencollective.com/meshtastic/)
|
|
||||||
[](https://vercel.com?utm_source=meshtastic&utm_campaign=oss)
|
|
||||||
|
|
||||||
<a href="https://trendshift.io/repositories/5524" target="_blank"><img src="https://trendshift.io/api/badge/repositories/5524" alt="meshtastic%2Ffirmware | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
Поставить регион "NO_LIMITS" можно только через настройки на самом устроистве или через консольные команды (https://meshtastic.org/docs/software/python/cli/). Новый регион имеет индекс 27
|
||||||
|
|
||||||
</div>
|
Данная модификация предназначена исключительно для ознакомления и тестирования в пределах вашей собственной сети :)
|
||||||
|
|
||||||
</div>
|
## Сборка и прошивка
|
||||||
|
|
||||||
<div align="center">
|
Создаем виртуальное оружение
|
||||||
<a href="https://meshtastic.org">Website</a>
|
|
||||||
-
|
|
||||||
<a href="https://meshtastic.org/docs/">Documentation</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## Overview
|
```
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
This repository contains the official device firmware for Meshtastic, an open-source LoRa mesh networking project designed for long-range, low-power communication without relying on internet or cellular infrastructure. The firmware supports various hardware platforms, including ESP32, nRF52, RP2040/RP2350, and Linux-based devices.
|
Устанавливаем PlatformIO
|
||||||
|
|
||||||
Meshtastic enables text messaging, location sharing, and telemetry over a decentralized mesh network, making it ideal for outdoor adventures, emergency preparedness, and remote operations.
|
```
|
||||||
|
pip install platformio
|
||||||
|
```
|
||||||
|
|
||||||
### Get Started
|
Собираем прошивку (пример для heltec v3)
|
||||||
|
|
||||||
- 🔧 **[Building Instructions](https://meshtastic.org/docs/development/firmware/build)** – Learn how to compile the firmware from source.
|
```
|
||||||
- ⚡ **[Flashing Instructions](https://meshtastic.org/docs/getting-started/flashing-firmware/)** – Install or update the firmware on your device.
|
pio run -e heltec-v3
|
||||||
|
```
|
||||||
|
|
||||||
Join our community and help improve Meshtastic! 🚀
|
После успешной сборки прошивка (firmware.bin) будет лежать в папке .pio/build/{название платы}
|
||||||
|
|
||||||
|
Для того чтобы залить прошивку можно воспользоваться официальным веб инструментом: https://flasher.meshtastic.org/, или через CLI https://meshtastic.org/docs/getting-started/flashing-firmware/esp32/cli-script/, но тут уже только бог в помощь
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Ссылка на оригинальный репозиторий: https://github.com/meshtastic/firmware
|
||||||
|
|
||||||
## Stats
|
|
||||||
|
|
||||||

|
|
||||||
|
|||||||
@ -57,7 +57,7 @@ lib_deps =
|
|||||||
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
||||||
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
|
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
|
||||||
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
|
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
|
||||||
https://github.com/lewisxhe/XPowersLib/archive/v0.3.2.zip
|
https://github.com/lewisxhe/XPowersLib/archive/v0.3.1.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||||
@ -28,7 +28,7 @@ lib_deps =
|
|||||||
${environmental_extra.lib_deps}
|
${environmental_extra.lib_deps}
|
||||||
${radiolib_base.lib_deps}
|
${radiolib_base.lib_deps}
|
||||||
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||||
lewisxhe/XPowersLib@0.3.2
|
lewisxhe/XPowersLib@0.3.1
|
||||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||||
@ -7,13 +7,13 @@ extends = arduino_base
|
|||||||
platform_packages =
|
platform_packages =
|
||||||
; our custom Git version until they merge our PR
|
; our custom Git version until they merge our PR
|
||||||
# TODO renovate
|
# TODO renovate
|
||||||
platformio/framework-arduinoadafruitnrf52 @ https://github.com/meshtastic/Adafruit_nRF52_Arduino#c770c8a16a351b55b86e347a3d9d7b74ad0bbf39
|
platformio/framework-arduinoadafruitnrf52 @ https://github.com/meshtastic/Adafruit_nRF52_Arduino#e13f5820002a4fb2a5e6754b42ace185277e5adf
|
||||||
; Don't renovate toolchain-gccarmnoneeabi
|
; Don't renovate toolchain-gccarmnoneeabi
|
||||||
platformio/toolchain-gccarmnoneeabi@~1.90301.0
|
platformio/toolchain-gccarmnoneeabi@~1.90301.0
|
||||||
|
|
||||||
build_type = release
|
build_type = debug
|
||||||
build_flags =
|
build_flags =
|
||||||
-include variants/nrf52840/cpp_overrides/lfs_util.h
|
-include arch/nrf52/cpp_overrides/lfs_util.h
|
||||||
${arduino_base.build_flags}
|
${arduino_base.build_flags}
|
||||||
-DSERIAL_BUFFER_SIZE=1024
|
-DSERIAL_BUFFER_SIZE=1024
|
||||||
-Wno-unused-variable
|
-Wno-unused-variable
|
||||||
@ -21,17 +21,6 @@ build_flags =
|
|||||||
-DLFS_NO_ASSERT ; Disable LFS assertions , see https://github.com/meshtastic/firmware/pull/3818
|
-DLFS_NO_ASSERT ; Disable LFS assertions , see https://github.com/meshtastic/firmware/pull/3818
|
||||||
-DMESHTASTIC_EXCLUDE_AUDIO=1
|
-DMESHTASTIC_EXCLUDE_AUDIO=1
|
||||||
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
|
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
|
||||||
-Os
|
|
||||||
build_unflags =
|
|
||||||
-Ofast
|
|
||||||
-Og
|
|
||||||
-ggdb3
|
|
||||||
-ggdb2
|
|
||||||
-g3
|
|
||||||
-g2
|
|
||||||
-g
|
|
||||||
-g1
|
|
||||||
-g0
|
|
||||||
|
|
||||||
build_src_filter =
|
build_src_filter =
|
||||||
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2xx0> -<mesh/eth/> -<mesh/raspihttp> -<serialization/>
|
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2xx0> -<mesh/eth/> -<mesh/raspihttp> -<serialization/>
|
||||||
@ -8,7 +8,7 @@ lib_deps =
|
|||||||
${environmental_base.lib_deps}
|
${environmental_base.lib_deps}
|
||||||
${environmental_extra.lib_deps}
|
${environmental_extra.lib_deps}
|
||||||
# renovate: datasource=git-refs depName=Kongduino-Adafruit_nRFCrypto packageName=https://github.com/Kongduino/Adafruit_nRFCrypto gitBranch=master
|
# renovate: datasource=git-refs depName=Kongduino-Adafruit_nRFCrypto packageName=https://github.com/Kongduino/Adafruit_nRFCrypto gitBranch=master
|
||||||
https://github.com/Kongduino/Adafruit_nRFCrypto/archive/8cde7189b5ead9dcd49f72601b43b969c0bbc06e.zip
|
https://github.com/Kongduino/Adafruit_nRFCrypto/archive/5f838d2709461a2c981f642917aa50254a25c14c.zip
|
||||||
|
|
||||||
; Common NRF52 debugging settings follow. See the Meshtastic developer docs for how to connect SWD debugging probes to your board.
|
; Common NRF52 debugging settings follow. See the Meshtastic developer docs for how to connect SWD debugging probes to your board.
|
||||||
|
|
||||||
@ -2,7 +2,7 @@
|
|||||||
extends = arduino_base
|
extends = arduino_base
|
||||||
platform =
|
platform =
|
||||||
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
|
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
|
||||||
platformio/ststm32@19.4.0
|
platformio/ststm32@19.3.0
|
||||||
platform_packages =
|
platform_packages =
|
||||||
# TODO renovate
|
# TODO renovate
|
||||||
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
|
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
|
||||||
@ -1,165 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Summarise linker map output to highlight heavy object files and libraries.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
python bin/analyze_map.py --map .pio/build/rak4631/output.map --top 20
|
|
||||||
|
|
||||||
The script parses GNU ld map files and aggregates section sizes per object file
|
|
||||||
and per archive/library, then prints sortable tables that make it easy to spot
|
|
||||||
modules worth trimming or hiding behind feature flags.
|
|
||||||
"""
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import collections
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from typing import DefaultDict, Dict, Tuple
|
|
||||||
|
|
||||||
|
|
||||||
SECTION_LINE_RE = re.compile(r"^\s+(?P<section>\S+)\s+0x[0-9A-Fa-f]+\s+0x(?P<size>[0-9A-Fa-f]+)\s+(?P<object>.+)$")
|
|
||||||
ARCHIVE_MEMBER_RE = re.compile(r"^(?P<archive>.+)\((?P<object>[^)]+)\)$")
|
|
||||||
|
|
||||||
|
|
||||||
def human_size(num_bytes: int) -> str:
|
|
||||||
"""Return a friendly size string with one decimal place."""
|
|
||||||
if num_bytes < 1024:
|
|
||||||
return f"{num_bytes:,} B"
|
|
||||||
num = float(num_bytes)
|
|
||||||
for unit in ("KB", "MB", "GB"):
|
|
||||||
num /= 1024.0
|
|
||||||
if num < 1024.0:
|
|
||||||
return f"{num:.1f} {unit}"
|
|
||||||
return f"{num:.1f} TB"
|
|
||||||
|
|
||||||
|
|
||||||
def shorten_path(path: str, root: str) -> str:
|
|
||||||
"""Prefer repository-relative paths for readability."""
|
|
||||||
path = path.strip()
|
|
||||||
if not path:
|
|
||||||
return path
|
|
||||||
|
|
||||||
# Normalise Windows archives (backslashes) to POSIX style for consistency.
|
|
||||||
path = path.replace("\\", "/")
|
|
||||||
|
|
||||||
# Attempt to strip the root when an absolute path lives inside the repo.
|
|
||||||
if os.path.isabs(path):
|
|
||||||
try:
|
|
||||||
rel = os.path.relpath(path, root)
|
|
||||||
if not rel.startswith(".."):
|
|
||||||
return rel
|
|
||||||
except ValueError:
|
|
||||||
# relpath can fail on mixed drives on Windows; fall back to basename.
|
|
||||||
pass
|
|
||||||
return path
|
|
||||||
|
|
||||||
|
|
||||||
def describe_object(raw_object: str, root: str) -> Tuple[str, str]:
|
|
||||||
"""Return a human friendly object label and the library it belongs to."""
|
|
||||||
raw_object = raw_object.strip()
|
|
||||||
lib_label = "[app]"
|
|
||||||
match = ARCHIVE_MEMBER_RE.match(raw_object)
|
|
||||||
if match:
|
|
||||||
archive = shorten_path(match.group("archive"), root)
|
|
||||||
obj = match.group("object")
|
|
||||||
lib_label = os.path.basename(archive) or archive
|
|
||||||
label = f"{archive}:{obj}"
|
|
||||||
else:
|
|
||||||
label = shorten_path(raw_object, root)
|
|
||||||
# If the object lives under libs, hint at the containing directory.
|
|
||||||
parent = os.path.basename(os.path.dirname(label))
|
|
||||||
if parent:
|
|
||||||
lib_label = parent
|
|
||||||
return label, lib_label
|
|
||||||
|
|
||||||
|
|
||||||
def parse_map(map_path: str, repo_root: str) -> Tuple[Dict[str, int], Dict[str, int], Dict[str, Dict[str, int]]]:
|
|
||||||
per_object: DefaultDict[str, int] = collections.defaultdict(int)
|
|
||||||
per_library: DefaultDict[str, int] = collections.defaultdict(int)
|
|
||||||
per_object_sections: DefaultDict[str, DefaultDict[str, int]] = collections.defaultdict(lambda: collections.defaultdict(int))
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(map_path, "r", encoding="utf-8", errors="ignore") as handle:
|
|
||||||
for line in handle:
|
|
||||||
match = SECTION_LINE_RE.match(line)
|
|
||||||
if not match:
|
|
||||||
continue
|
|
||||||
|
|
||||||
section = match.group("section")
|
|
||||||
if section.startswith("*") or section in {"LOAD", "ORIGIN"}:
|
|
||||||
continue
|
|
||||||
|
|
||||||
size = int(match.group("size"), 16)
|
|
||||||
if size == 0:
|
|
||||||
continue
|
|
||||||
|
|
||||||
obj_token = match.group("object").strip()
|
|
||||||
if not obj_token or obj_token.startswith("*") or "load address" in obj_token:
|
|
||||||
continue
|
|
||||||
|
|
||||||
label, lib_label = describe_object(obj_token, repo_root)
|
|
||||||
per_object[label] += size
|
|
||||||
per_library[lib_label] += size
|
|
||||||
per_object_sections[label][section] += size
|
|
||||||
except FileNotFoundError:
|
|
||||||
raise SystemExit(f"error: map file '{map_path}' not found. Run a build first.")
|
|
||||||
|
|
||||||
return per_object, per_library, per_object_sections
|
|
||||||
|
|
||||||
|
|
||||||
def format_section_breakdown(section_sizes: Dict[str, int], total: int, limit: int = 3) -> str:
|
|
||||||
items = sorted(section_sizes.items(), key=lambda kv: kv[1], reverse=True)
|
|
||||||
parts = []
|
|
||||||
for section, size in items[:limit]:
|
|
||||||
pct = (size / total) * 100 if total else 0
|
|
||||||
parts.append(f"{section} {pct:.1f}%")
|
|
||||||
if len(items) > limit:
|
|
||||||
remainder = total - sum(size for _, size in items[:limit])
|
|
||||||
pct = (remainder / total) * 100 if total else 0
|
|
||||||
parts.append(f"other {pct:.1f}%")
|
|
||||||
return ", ".join(parts)
|
|
||||||
|
|
||||||
|
|
||||||
def print_report(map_path: str, top_n: int, per_object: Dict[str, int], per_library: Dict[str, int], per_object_sections: Dict[str, Dict[str, int]]):
|
|
||||||
total_bytes = sum(per_object.values())
|
|
||||||
if total_bytes == 0:
|
|
||||||
print("No section data found in map file.")
|
|
||||||
return
|
|
||||||
|
|
||||||
print(f"Map file: {map_path}")
|
|
||||||
print(f"Accounted size: {human_size(total_bytes)} across {len(per_object)} object files\n")
|
|
||||||
|
|
||||||
sorted_objects = sorted(per_object.items(), key=lambda kv: kv[1], reverse=True)
|
|
||||||
print(f"Top {min(top_n, len(sorted_objects))} object files by linked size:")
|
|
||||||
for idx, (obj, size) in enumerate(sorted_objects[:top_n], 1):
|
|
||||||
pct = (size / total_bytes) * 100
|
|
||||||
breakdown = format_section_breakdown(per_object_sections[obj], size)
|
|
||||||
print(f"{idx:2}. {human_size(size):>9} ({size:,} B, {pct:5.2f}% of linked size)")
|
|
||||||
print(f" {obj}")
|
|
||||||
if breakdown:
|
|
||||||
print(f" sections: {breakdown}")
|
|
||||||
print()
|
|
||||||
|
|
||||||
sorted_libs = sorted(per_library.items(), key=lambda kv: kv[1], reverse=True)
|
|
||||||
print(f"Top {min(top_n, len(sorted_libs))} libraries or source roots:")
|
|
||||||
for idx, (lib, size) in enumerate(sorted_libs[:top_n], 1):
|
|
||||||
pct = (size / total_bytes) * 100
|
|
||||||
print(f"{idx:2}. {human_size(size):>9} ({size:,} B, {pct:5.2f}% of linked size) {lib}")
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
parser = argparse.ArgumentParser(description="Highlight heavy object files from a GNU ld map file.")
|
|
||||||
parser.add_argument("--map", default=".pio/build/rak4631/output.map", help="Path to the map file (default: %(default)s)")
|
|
||||||
parser.add_argument("--top", type=int, default=20, help="Number of entries to display per table (default: %(default)s)")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
map_path = os.path.abspath(args.map)
|
|
||||||
repo_root = os.path.abspath(os.getcwd())
|
|
||||||
|
|
||||||
per_object, per_library, per_object_sections = parse_map(map_path, repo_root)
|
|
||||||
print_report(os.path.relpath(map_path, repo_root), args.top, per_object, per_library, per_object_sections)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@ -87,12 +87,6 @@
|
|||||||
</screenshots>
|
</screenshots>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
<release version="2.7.17" date="2025-11-28">
|
|
||||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.17</url>
|
|
||||||
</release>
|
|
||||||
<release version="2.7.16" date="2025-11-19">
|
|
||||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.16</url>
|
|
||||||
</release>
|
|
||||||
<release version="2.7.15" date="2025-11-13">
|
<release version="2.7.15" date="2025-11-13">
|
||||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.15</url>
|
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.15</url>
|
||||||
</release>
|
</release>
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# trunk-ignore-all(ruff/F821)
|
|
||||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
|
||||||
Import("env")
|
|
||||||
platform = env.PioPlatform()
|
|
||||||
|
|
||||||
if platform.name == "native":
|
|
||||||
env.Replace(PROGNAME="meshtasticd")
|
|
||||||
else:
|
|
||||||
from readprops import readProps
|
|
||||||
prefsLoc = env["PROJECT_DIR"] + "/version.properties"
|
|
||||||
verObj = readProps(prefsLoc)
|
|
||||||
env.Replace(PROGNAME=f"firmware-{env.get('PIOENV')}-{verObj['long']}")
|
|
||||||
|
|
||||||
# Print the new program name for verification
|
|
||||||
print(f"PROGNAME: {env.get('PROGNAME')}")
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
{
|
|
||||||
"build": {
|
|
||||||
"arduino": {
|
|
||||||
"ldscript": "nrf52840_s140_v6.ld"
|
|
||||||
},
|
|
||||||
"core": "nRF5",
|
|
||||||
"cpu": "cortex-m4",
|
|
||||||
"extra_flags": "-DNRF52840_XXAA",
|
|
||||||
"f_cpu": "64000000L",
|
|
||||||
"hwids": [
|
|
||||||
["0x239A", "0x4405"],
|
|
||||||
["0x239A", "0x0029"],
|
|
||||||
["0x239A", "0x002A"]
|
|
||||||
],
|
|
||||||
"usb_product": "elecrow_eink",
|
|
||||||
"mcu": "nrf52840",
|
|
||||||
"variant": "ELECROW-ThinkNode-M3",
|
|
||||||
"variants_dir": "variants",
|
|
||||||
"bsp": {
|
|
||||||
"name": "adafruit"
|
|
||||||
},
|
|
||||||
"softdevice": {
|
|
||||||
"sd_flags": "-DS140",
|
|
||||||
"sd_name": "s140",
|
|
||||||
"sd_version": "6.1.1",
|
|
||||||
"sd_fwid": "0x00B6"
|
|
||||||
},
|
|
||||||
"bootloader": {
|
|
||||||
"settings_addr": "0xFF000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"connectivity": ["bluetooth"],
|
|
||||||
"debug": {
|
|
||||||
"jlink_device": "nRF52840_xxAA",
|
|
||||||
"onboard_tools": ["jlink"],
|
|
||||||
"svd_path": "nrf52840.svd",
|
|
||||||
"openocd_target": "nrf52840-mdk-rs"
|
|
||||||
},
|
|
||||||
"frameworks": ["arduino"],
|
|
||||||
"name": "elecrow nrf",
|
|
||||||
"upload": {
|
|
||||||
"maximum_ram_size": 248832,
|
|
||||||
"maximum_size": 815104,
|
|
||||||
"speed": 115200,
|
|
||||||
"protocol": "nrfutil",
|
|
||||||
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
|
|
||||||
"use_1200bps_touch": true,
|
|
||||||
"require_upload_port": true,
|
|
||||||
"wait_for_upload_port": true
|
|
||||||
},
|
|
||||||
"url": "",
|
|
||||||
"vendor": "ELECROW"
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
{
|
|
||||||
"build": {
|
|
||||||
"arduino": {
|
|
||||||
"ldscript": "nrf52840_s140_v6.ld"
|
|
||||||
},
|
|
||||||
"core": "nRF5",
|
|
||||||
"cpu": "cortex-m4",
|
|
||||||
"extra_flags": "-DARDUINO_NRF52840_ELECROW_M6 -DNRF52840_XXAA",
|
|
||||||
"f_cpu": "64000000L",
|
|
||||||
"hwids": [
|
|
||||||
["0x239A", "0x4405"],
|
|
||||||
["0x239A", "0x0029"],
|
|
||||||
["0x239A", "0x002A"]
|
|
||||||
],
|
|
||||||
"usb_product": "elecrow_thinknode_m6",
|
|
||||||
"mcu": "nrf52840",
|
|
||||||
"variant": "ELECROW-ThinkNode-M6",
|
|
||||||
"variants_dir": "variants",
|
|
||||||
"bsp": {
|
|
||||||
"name": "adafruit"
|
|
||||||
},
|
|
||||||
"softdevice": {
|
|
||||||
"sd_flags": "-DS140",
|
|
||||||
"sd_name": "s140",
|
|
||||||
"sd_version": "6.1.1",
|
|
||||||
"sd_fwid": "0x00B6"
|
|
||||||
},
|
|
||||||
"bootloader": {
|
|
||||||
"settings_addr": "0xFF000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"connectivity": ["bluetooth"],
|
|
||||||
"debug": {
|
|
||||||
"jlink_device": "nRF52840_xxAA",
|
|
||||||
"onboard_tools": ["jlink"],
|
|
||||||
"svd_path": "nrf52840.svd",
|
|
||||||
"openocd_target": "nrf52840-mdk-rs"
|
|
||||||
},
|
|
||||||
"frameworks": ["arduino"],
|
|
||||||
"name": "ELECROW ThinkNode M6",
|
|
||||||
"upload": {
|
|
||||||
"maximum_ram_size": 248832,
|
|
||||||
"maximum_size": 815104,
|
|
||||||
"speed": 115200,
|
|
||||||
"protocol": "nrfutil",
|
|
||||||
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
|
|
||||||
"use_1200bps_touch": true,
|
|
||||||
"require_upload_port": true,
|
|
||||||
"wait_for_upload_port": true
|
|
||||||
},
|
|
||||||
"url": "https://www.elecrow.com/thinknode-m6-outdoor-solar-power-for-lora-powered-by-nrf52840-supports-gps.html",
|
|
||||||
"vendor": "ELECROW"
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"build": {
|
|
||||||
"arduino": {
|
|
||||||
"ldscript": "esp32s3_out.ld",
|
|
||||||
"memory_type": "qio_opi"
|
|
||||||
},
|
|
||||||
"core": "esp32",
|
|
||||||
"extra_flags": [
|
|
||||||
"-DBOARD_HAS_PSRAM",
|
|
||||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
|
||||||
"-DARDUINO_USB_MODE=0",
|
|
||||||
"-DARDUINO_RUNNING_CORE=1",
|
|
||||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
|
||||||
],
|
|
||||||
"f_cpu": "240000000L",
|
|
||||||
"f_flash": "80000000L",
|
|
||||||
"flash_mode": "qio",
|
|
||||||
"hwids": [["0x303A", "0x1001"]],
|
|
||||||
"mcu": "esp32s3",
|
|
||||||
"variant": "hackaday-communicator"
|
|
||||||
},
|
|
||||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
|
||||||
"debug": {
|
|
||||||
"default_tool": "esp-builtin",
|
|
||||||
"onboard_tools": ["esp-builtin"],
|
|
||||||
"openocd_target": "esp32s3.cfg"
|
|
||||||
},
|
|
||||||
"frameworks": ["arduino", "espidf"],
|
|
||||||
"name": "hackaday-communicator (16 MB FLASH, 8 MB PSRAM)",
|
|
||||||
"upload": {
|
|
||||||
"flash_size": "16MB",
|
|
||||||
"maximum_ram_size": 327680,
|
|
||||||
"maximum_size": 16777216,
|
|
||||||
"use_1200bps_touch": true,
|
|
||||||
"wait_for_upload_port": true,
|
|
||||||
"require_upload_port": true,
|
|
||||||
"speed": 1500000
|
|
||||||
},
|
|
||||||
"url": "hackaday.com",
|
|
||||||
"vendor": "hackaday"
|
|
||||||
}
|
|
||||||
@ -9,7 +9,7 @@
|
|||||||
"extra_flags": [
|
"extra_flags": [
|
||||||
"-DBOARD_HAS_PSRAM",
|
"-DBOARD_HAS_PSRAM",
|
||||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||||
"-DARDUINO_USB_MODE=1",
|
"-DARDUINO_USB_MODE=0",
|
||||||
"-DARDUINO_RUNNING_CORE=1",
|
"-DARDUINO_RUNNING_CORE=1",
|
||||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,56 +0,0 @@
|
|||||||
{
|
|
||||||
"build": {
|
|
||||||
"arduino": {
|
|
||||||
"ldscript": "nrf52840_s140_v6.ld"
|
|
||||||
},
|
|
||||||
"core": "nRF5",
|
|
||||||
"cpu": "cortex-m4",
|
|
||||||
"extra_flags": "-DARDUINO_NRF52840_MUZI_BASE -DNRF52840_XXAA",
|
|
||||||
"f_cpu": "64000000L",
|
|
||||||
"hwids": [["0x239A", "0xcafe"]],
|
|
||||||
"mcu": "nrf52840",
|
|
||||||
"variant": "muzi-base",
|
|
||||||
"variants_dir": "variants",
|
|
||||||
"bsp": {
|
|
||||||
"name": "adafruit"
|
|
||||||
},
|
|
||||||
"softdevice": {
|
|
||||||
"sd_flags": "-DS140",
|
|
||||||
"sd_name": "s140",
|
|
||||||
"sd_version": "6.1.1",
|
|
||||||
"sd_fwid": "0x00B6"
|
|
||||||
},
|
|
||||||
"bootloader": {
|
|
||||||
"settings_addr": "0xFF000"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"connectivity": ["bluetooth"],
|
|
||||||
"debug": {
|
|
||||||
"jlink_device": "nRF52840_xxAA",
|
|
||||||
"onboard_tools": ["jlink"],
|
|
||||||
"svd_path": "nrf52840.svd",
|
|
||||||
"openocd_target": "nrf52840-mdk-rs"
|
|
||||||
},
|
|
||||||
"frameworks": ["arduino"],
|
|
||||||
"name": "Muzi Base",
|
|
||||||
"url": "https://muzi.works/",
|
|
||||||
"vendor": "MuziWorks",
|
|
||||||
"upload": {
|
|
||||||
"maximum_ram_size": 248832,
|
|
||||||
"maximum_size": 815104,
|
|
||||||
"speed": 115200,
|
|
||||||
"protocol": "nrfutil",
|
|
||||||
"protocols": [
|
|
||||||
"jlink",
|
|
||||||
"nrfjprog",
|
|
||||||
"nrfutil",
|
|
||||||
"blackmagic",
|
|
||||||
"cmsis-dap",
|
|
||||||
"mbed",
|
|
||||||
"stlink"
|
|
||||||
],
|
|
||||||
"use_1200bps_touch": true,
|
|
||||||
"require_upload_port": true,
|
|
||||||
"wait_for_upload_port": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
13
debian/changelog
vendored
13
debian/changelog
vendored
@ -1,16 +1,3 @@
|
|||||||
meshtasticd (2.7.17.0) unstable; urgency=medium
|
|
||||||
|
|
||||||
* Version 2.7.17
|
|
||||||
|
|
||||||
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Fri, 28 Nov 2025 15:11:34 +0000
|
|
||||||
|
|
||||||
meshtasticd (2.7.16.0) unstable; urgency=medium
|
|
||||||
|
|
||||||
* Version 2.7.16
|
|
||||||
|
|
||||||
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 19 Nov 2025 16:12:32 +0000
|
|
||||||
|
|
||||||
|
|
||||||
meshtasticd (2.7.15.0) unstable; urgency=medium
|
meshtasticd (2.7.15.0) unstable; urgency=medium
|
||||||
|
|
||||||
* Version 2.7.15
|
* Version 2.7.15
|
||||||
|
|||||||
@ -1,80 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# trunk-ignore-all(ruff/F821)
|
|
||||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
|
||||||
# trunk-ignore-all(ruff/E402): Hacky esptool import
|
|
||||||
# trunk-ignore-all(flake8/E402): Hacky esptool import
|
|
||||||
import sys
|
|
||||||
from os.path import join
|
|
||||||
|
|
||||||
Import("env")
|
|
||||||
platform = env.PioPlatform()
|
|
||||||
|
|
||||||
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
|
|
||||||
import esptool
|
|
||||||
|
|
||||||
|
|
||||||
def esp32_create_combined_bin(source, target, env):
|
|
||||||
# this sub is borrowed from ESPEasy build toolchain. It's licensed under GPL V3
|
|
||||||
# https://github.com/letscontrolit/ESPEasy/blob/mega/tools/pio/post_esp32.py
|
|
||||||
print("Generating combined binary for serial flashing")
|
|
||||||
|
|
||||||
app_offset = 0x10000
|
|
||||||
|
|
||||||
new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.factory.bin")
|
|
||||||
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
|
|
||||||
firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin")
|
|
||||||
chip = env.get("BOARD_MCU")
|
|
||||||
board = env.BoardConfig()
|
|
||||||
flash_size = board.get("upload.flash_size")
|
|
||||||
flash_freq = board.get("build.f_flash", "40m")
|
|
||||||
flash_freq = flash_freq.replace("000000L", "m")
|
|
||||||
flash_mode = board.get("build.flash_mode", "dio")
|
|
||||||
memory_type = board.get("build.arduino.memory_type", "qio_qspi")
|
|
||||||
if flash_mode == "qio" or flash_mode == "qout":
|
|
||||||
flash_mode = "dio"
|
|
||||||
if memory_type == "opi_opi" or memory_type == "opi_qspi":
|
|
||||||
flash_mode = "dout"
|
|
||||||
cmd = [
|
|
||||||
"--chip",
|
|
||||||
chip,
|
|
||||||
"merge_bin",
|
|
||||||
"-o",
|
|
||||||
new_file_name,
|
|
||||||
"--flash_mode",
|
|
||||||
flash_mode,
|
|
||||||
"--flash_freq",
|
|
||||||
flash_freq,
|
|
||||||
"--flash_size",
|
|
||||||
flash_size,
|
|
||||||
]
|
|
||||||
|
|
||||||
print(" Offset | File")
|
|
||||||
for section in sections:
|
|
||||||
sect_adr, sect_file = section.split(" ", 1)
|
|
||||||
print(f" - {sect_adr} | {sect_file}")
|
|
||||||
cmd += [sect_adr, sect_file]
|
|
||||||
|
|
||||||
print(f" - {hex(app_offset)} | {firmware_name}")
|
|
||||||
cmd += [hex(app_offset), firmware_name]
|
|
||||||
|
|
||||||
print("Using esptool.py arguments: %s" % " ".join(cmd))
|
|
||||||
|
|
||||||
esptool.main(cmd)
|
|
||||||
|
|
||||||
|
|
||||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)
|
|
||||||
|
|
||||||
esp32_kind = env.GetProjectOption("custom_esp32_kind")
|
|
||||||
if esp32_kind == "esp32":
|
|
||||||
# Free up some IRAM by removing auxiliary SPI flash chip drivers.
|
|
||||||
# Wrapped stub symbols are defined in src/platform/esp32/iram-quirk.c.
|
|
||||||
env.Append(
|
|
||||||
LINKFLAGS=[
|
|
||||||
"-Wl,--wrap=esp_flash_chip_gd",
|
|
||||||
"-Wl,--wrap=esp_flash_chip_issi",
|
|
||||||
"-Wl,--wrap=esp_flash_chip_winbond",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# For newer ESP32 targets, using newlib nano works better.
|
|
||||||
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
|
|
||||||
@ -1,73 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# trunk-ignore-all(ruff/F821)
|
|
||||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
|
||||||
import json
|
|
||||||
import sys
|
|
||||||
from os.path import isfile
|
|
||||||
|
|
||||||
Import("env")
|
|
||||||
|
|
||||||
|
|
||||||
# From https://github.com/platformio/platform-espressif32/blob/develop/builder/main.py
|
|
||||||
def _parse_size(value):
|
|
||||||
if isinstance(value, int):
|
|
||||||
return value
|
|
||||||
elif value.isdigit():
|
|
||||||
return int(value)
|
|
||||||
elif value.startswith("0x"):
|
|
||||||
return int(value, 16)
|
|
||||||
elif value[-1].upper() in ("K", "M"):
|
|
||||||
base = 1024 if value[-1].upper() == "K" else 1024 * 1024
|
|
||||||
return int(value[:-1]) * base
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_partitions(env):
|
|
||||||
partitions_csv = env.subst("$PARTITIONS_TABLE_CSV")
|
|
||||||
if not isfile(partitions_csv):
|
|
||||||
sys.stderr.write(
|
|
||||||
"Could not find the file %s with partitions " "table.\n" % partitions_csv
|
|
||||||
)
|
|
||||||
env.Exit(1)
|
|
||||||
return
|
|
||||||
|
|
||||||
result = []
|
|
||||||
# The first offset is 0x9000 because partition table is flashed to 0x8000 and
|
|
||||||
# occupies an entire flash sector, which size is 0x1000
|
|
||||||
next_offset = 0x9000
|
|
||||||
with open(partitions_csv) as fp:
|
|
||||||
for line in fp.readlines():
|
|
||||||
line = line.strip()
|
|
||||||
if not line or line.startswith("#"):
|
|
||||||
continue
|
|
||||||
tokens = [t.strip() for t in line.split(",")]
|
|
||||||
if len(tokens) < 5:
|
|
||||||
continue
|
|
||||||
|
|
||||||
bound = 0x10000 if tokens[1] in ("0", "app") else 4
|
|
||||||
calculated_offset = (next_offset + bound - 1) & ~(bound - 1)
|
|
||||||
partition = {
|
|
||||||
"name": tokens[0],
|
|
||||||
"type": tokens[1],
|
|
||||||
"subtype": tokens[2],
|
|
||||||
"offset": tokens[3] or calculated_offset,
|
|
||||||
"size": tokens[4],
|
|
||||||
"flags": tokens[5] if len(tokens) > 5 else None,
|
|
||||||
}
|
|
||||||
result.append(partition)
|
|
||||||
next_offset = _parse_size(partition["offset"]) + _parse_size(
|
|
||||||
partition["size"]
|
|
||||||
)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def mtjson_esp32_part(target, source, env):
|
|
||||||
part = _parse_partitions(env)
|
|
||||||
pj = json.dumps(part)
|
|
||||||
# print(f"JSON_PARTITIONS: {pj}")
|
|
||||||
# Dump json string to 'custom_mtjson_part' variable to use later when writing the manifest
|
|
||||||
env.Replace(custom_mtjson_part=pj)
|
|
||||||
|
|
||||||
|
|
||||||
env.AddPreAction("mtjson", mtjson_esp32_part)
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# trunk-ignore-all(ruff/F821)
|
|
||||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
|
||||||
|
|
||||||
import sys
|
|
||||||
from os.path import basename
|
|
||||||
|
|
||||||
Import("env")
|
|
||||||
|
|
||||||
|
|
||||||
# Custom HEX from ELF
|
|
||||||
# Convert hex to uf2 for nrf52
|
|
||||||
def nrf52_hex_to_uf2(source, target, env):
|
|
||||||
hex_path = target[0].get_abspath()
|
|
||||||
# When using merged hex, drop 'merged' from uf2 filename
|
|
||||||
uf2_path = hex_path.replace(".merged.", ".")
|
|
||||||
uf2_path = uf2_path.replace(".hex", ".uf2")
|
|
||||||
env.Execute(
|
|
||||||
env.VerboseAction(
|
|
||||||
f'"{sys.executable}" ./bin/uf2conv.py "{hex_path}" -c -f 0xADA52840 -o "{uf2_path}"',
|
|
||||||
f"Generating UF2 file from {basename(hex_path)}",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def nrf52_mergehex(source, target, env):
|
|
||||||
hex_path = target[0].get_abspath()
|
|
||||||
merged_hex_path = hex_path.replace(".hex", ".merged.hex")
|
|
||||||
merge_with = None
|
|
||||||
if "wio-sdk-wm1110" == str(env.get("PIOENV")):
|
|
||||||
merge_with = env.subst("$PROJECT_DIR/bin/s140_nrf52_7.3.0_softdevice.hex")
|
|
||||||
else:
|
|
||||||
print("merge_with not defined for this target")
|
|
||||||
|
|
||||||
if merge_with is not None:
|
|
||||||
env.Execute(
|
|
||||||
env.VerboseAction(
|
|
||||||
f'"$PROJECT_DIR/bin/mergehex" -m "{hex_path}" "{merge_with}" -o "{merged_hex_path}"',
|
|
||||||
"Merging HEX with SoftDevice",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
print(f'Merged file saved at "{basename(merged_hex_path)}"')
|
|
||||||
nrf52_hex_to_uf2([hex_path, merge_with], [env.File(merged_hex_path)], env)
|
|
||||||
|
|
||||||
|
|
||||||
# if WM1110 target, merge hex with softdevice 7.3.0
|
|
||||||
if "wio-sdk-wm1110" == env.get("PIOENV"):
|
|
||||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", nrf52_mergehex)
|
|
||||||
else:
|
|
||||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", nrf52_hex_to_uf2)
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# trunk-ignore-all(ruff/F821)
|
|
||||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
|
||||||
|
|
||||||
Import("env")
|
|
||||||
|
|
||||||
# Custom HEX from ELF
|
|
||||||
env.AddPostAction(
|
|
||||||
"$BUILD_DIR/${PROGNAME}.elf",
|
|
||||||
env.VerboseAction(
|
|
||||||
" ".join(
|
|
||||||
[
|
|
||||||
"$OBJCOPY",
|
|
||||||
"-O",
|
|
||||||
"ihex",
|
|
||||||
"-R",
|
|
||||||
".eeprom",
|
|
||||||
"$BUILD_DIR/${PROGNAME}.elf",
|
|
||||||
"$BUILD_DIR/${PROGNAME}.hex",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
"Building $BUILD_DIR/${PROGNAME}.hex",
|
|
||||||
),
|
|
||||||
)
|
|
||||||
@ -33,6 +33,7 @@ BuildRequires: python3dist(grpcio[protobuf])
|
|||||||
BuildRequires: python3dist(grpcio-tools)
|
BuildRequires: python3dist(grpcio-tools)
|
||||||
BuildRequires: git-core
|
BuildRequires: git-core
|
||||||
BuildRequires: gcc-c++
|
BuildRequires: gcc-c++
|
||||||
|
BuildRequires: (glibc-devel >= 2.38) or pkgconfig(libbsd-overlay)
|
||||||
BuildRequires: pkgconfig(yaml-cpp)
|
BuildRequires: pkgconfig(yaml-cpp)
|
||||||
BuildRequires: pkgconfig(libgpiod)
|
BuildRequires: pkgconfig(libgpiod)
|
||||||
BuildRequires: pkgconfig(bluez)
|
BuildRequires: pkgconfig(bluez)
|
||||||
@ -49,13 +50,6 @@ BuildRequires: pkgconfig(x11)
|
|||||||
BuildRequires: pkgconfig(libinput)
|
BuildRequires: pkgconfig(libinput)
|
||||||
BuildRequires: pkgconfig(xkbcommon-x11)
|
BuildRequires: pkgconfig(xkbcommon-x11)
|
||||||
|
|
||||||
# libbsd is needed on older Fedora/RHEL to provide 'strlcpy'
|
|
||||||
%if 0%{?fedora} >= 39 || 0%{?rhel} >= 10
|
|
||||||
BuildRequires: glibc-devel >= 2.38
|
|
||||||
%else
|
|
||||||
BuildRequires: pkgconfig(libbsd-overlay)
|
|
||||||
%endif
|
|
||||||
|
|
||||||
Requires: systemd-udev
|
Requires: systemd-udev
|
||||||
|
|
||||||
%description
|
%description
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
default_envs = tbeam
|
default_envs = tbeam
|
||||||
|
|
||||||
extra_configs =
|
extra_configs =
|
||||||
variants/*/*.ini
|
arch/*/*.ini
|
||||||
variants/*/*/platformio.ini
|
variants/*/*/platformio.ini
|
||||||
variants/*/diy/*/platformio.ini
|
variants/*/diy/*/platformio.ini
|
||||||
src/graphics/niche/InkHUD/PlatformioConfig.ini
|
src/graphics/niche/InkHUD/PlatformioConfig.ini
|
||||||
@ -62,7 +62,7 @@ monitor_speed = 115200
|
|||||||
monitor_filters = direct
|
monitor_filters = direct
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
||||||
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/2887bf4a19f64d92c984dcc8fd5ca7429e425e4a.zip
|
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0cbc26b1f8f61957af0475f486b362eafe7cc4e2.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
||||||
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
||||||
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
||||||
@ -90,7 +90,7 @@ framework = arduino
|
|||||||
lib_deps =
|
lib_deps =
|
||||||
${env.lib_deps}
|
${env.lib_deps}
|
||||||
# renovate: datasource=custom.pio depName=NonBlockingRTTTL packageName=end2endzone/library/NonBlockingRTTTL
|
# renovate: datasource=custom.pio depName=NonBlockingRTTTL packageName=end2endzone/library/NonBlockingRTTTL
|
||||||
end2endzone/NonBlockingRTTTL@1.4.0
|
end2endzone/NonBlockingRTTTL@1.3.0
|
||||||
build_flags = ${env.build_flags} -Os
|
build_flags = ${env.build_flags} -Os
|
||||||
build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/>
|
build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/>
|
||||||
|
|
||||||
@ -121,7 +121,7 @@ lib_deps =
|
|||||||
[device-ui_base]
|
[device-ui_base]
|
||||||
lib_deps =
|
lib_deps =
|
||||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||||
https://github.com/meshtastic/device-ui/archive/3bf332240416c5cb8c919fac2a0ec7260eb3be75.zip
|
https://github.com/meshtastic/device-ui/archive/19b7855e9a1d9deff37391659ca7194e4ef57c43.zip
|
||||||
|
|
||||||
; Common libs for environmental measurements in telemetry module
|
; Common libs for environmental measurements in telemetry module
|
||||||
[environmental_base]
|
[environmental_base]
|
||||||
@ -169,7 +169,7 @@ lib_deps =
|
|||||||
# renovate: datasource=git-refs depName=DFRobot_RainfallSensor packageName=https://github.com/DFRobot/DFRobot_RainfallSensor gitBranch=master
|
# renovate: datasource=git-refs depName=DFRobot_RainfallSensor packageName=https://github.com/DFRobot/DFRobot_RainfallSensor gitBranch=master
|
||||||
https://github.com/DFRobot/DFRobot_RainfallSensor/archive/38fea5e02b40a5430be6dab39a99a6f6347d667e.zip
|
https://github.com/DFRobot/DFRobot_RainfallSensor/archive/38fea5e02b40a5430be6dab39a99a6f6347d667e.zip
|
||||||
# renovate: datasource=custom.pio depName=INA226 packageName=robtillaart/library/INA226
|
# renovate: datasource=custom.pio depName=INA226 packageName=robtillaart/library/INA226
|
||||||
robtillaart/INA226@0.6.5
|
robtillaart/INA226@0.6.4
|
||||||
# renovate: datasource=custom.pio depName=SparkFun MAX3010x packageName=sparkfun/library/SparkFun MAX3010x Pulse and Proximity Sensor Library
|
# renovate: datasource=custom.pio depName=SparkFun MAX3010x packageName=sparkfun/library/SparkFun MAX3010x Pulse and Proximity Sensor Library
|
||||||
sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2
|
sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2
|
||||||
# renovate: datasource=custom.pio depName=SparkFun 9DoF IMU Breakout ICM 20948 packageName=sparkfun/library/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library
|
# renovate: datasource=custom.pio depName=SparkFun 9DoF IMU Breakout ICM 20948 packageName=sparkfun/library/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library
|
||||||
@ -213,6 +213,6 @@ lib_deps =
|
|||||||
# renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
|
# renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
|
||||||
https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
|
https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
|
||||||
# renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
|
# renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
|
||||||
sensirion/Sensirion Core@0.7.2
|
sensirion/Sensirion Core@0.7.1
|
||||||
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
|
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
|
||||||
sensirion/Sensirion I2C SCD4x@1.1.0
|
sensirion/Sensirion I2C SCD4x@1.1.0
|
||||||
|
|||||||
@ -926,7 +926,7 @@ message Config {
|
|||||||
BR_902 = 26;
|
BR_902 = 26;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Custom unrestricted region
|
* NO LIMITS
|
||||||
*/
|
*/
|
||||||
NO_LIMITS = 27;
|
NO_LIMITS = 27;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -825,21 +825,6 @@ enum HardwareModel {
|
|||||||
*/
|
*/
|
||||||
RAK3401 = 117;
|
RAK3401 = 117;
|
||||||
|
|
||||||
/*
|
|
||||||
* RAK6421 Hat+
|
|
||||||
*/
|
|
||||||
RAK6421 = 118;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Elecrow ThinkNode M4
|
|
||||||
*/
|
|
||||||
THINKNODE_M4 = 119;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Elecrow ThinkNode M6
|
|
||||||
*/
|
|
||||||
THINKNODE_M6 = 120;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ------------------------------------------------------------------------------------------------------------------------------------------
|
* ------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
* Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
* Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||||
@ -1386,10 +1371,6 @@ message MeshPacket {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* The (immediate) destination for this packet
|
* The (immediate) destination for this packet
|
||||||
* If the value is 4,294,967,295 (maximum value of an unsigned 32bit integer), this indicates that the packet was
|
|
||||||
* not destined for a specific node, but for a channel as indicated by the value of `channel` below.
|
|
||||||
* If the value is another, this indicates that the packet was destined for a specific
|
|
||||||
* node (i.e. a kind of "Direct Message" to this node) and not broadcast on a channel.
|
|
||||||
*/
|
*/
|
||||||
fixed32 to = 2;
|
fixed32 to = 2;
|
||||||
|
|
||||||
|
|||||||
@ -50,11 +50,8 @@ class AudioThread : public concurrency::OSThread
|
|||||||
delete i2sRtttl;
|
delete i2sRtttl;
|
||||||
i2sRtttl = nullptr;
|
i2sRtttl = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rtttlFile != nullptr) {
|
|
||||||
delete rtttlFile;
|
delete rtttlFile;
|
||||||
rtttlFile = nullptr;
|
rtttlFile = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
setCPUFast(false);
|
setCPUFast(false);
|
||||||
#ifdef T_LORA_PAGER
|
#ifdef T_LORA_PAGER
|
||||||
@ -102,9 +99,9 @@ class AudioThread : public concurrency::OSThread
|
|||||||
};
|
};
|
||||||
|
|
||||||
AudioGeneratorRTTTL *i2sRtttl = nullptr;
|
AudioGeneratorRTTTL *i2sRtttl = nullptr;
|
||||||
AudioOutputI2S *audioOut = nullptr;
|
AudioOutputI2S *audioOut;
|
||||||
|
|
||||||
AudioFileSourcePROGMEM *rtttlFile = nullptr;
|
AudioFileSourcePROGMEM *rtttlFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -278,11 +278,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if defined(BATTERY_CHARGING_INV)
|
|
||||||
// bit of trickery to show 99% up until the charge finishes
|
|
||||||
if (!digitalRead(BATTERY_CHARGING_INV) && battery_SOC > 99)
|
|
||||||
battery_SOC = 99;
|
|
||||||
#endif
|
|
||||||
return clamp((int)(battery_SOC), 0, 100);
|
return clamp((int)(battery_SOC), 0, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,8 +455,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
}
|
}
|
||||||
// if it's not HIGH - check the battery
|
// if it's not HIGH - check the battery
|
||||||
#endif
|
#endif
|
||||||
#elif defined(MUZI_BASE)
|
|
||||||
return NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk;
|
|
||||||
#endif
|
#endif
|
||||||
return getBattVoltage() > chargingVolt;
|
return getBattVoltage() > chargingVolt;
|
||||||
}
|
}
|
||||||
@ -477,8 +470,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
#endif
|
#endif
|
||||||
#ifdef EXT_CHRG_DETECT
|
#ifdef EXT_CHRG_DETECT
|
||||||
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
|
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
|
||||||
#elif defined(BATTERY_CHARGING_INV)
|
|
||||||
return !digitalRead(BATTERY_CHARGING_INV);
|
|
||||||
#else
|
#else
|
||||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
|
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
|
||||||
if (hasINA()) {
|
if (hasINA()) {
|
||||||
@ -707,18 +698,11 @@ bool Power::setup()
|
|||||||
[]() {
|
[]() {
|
||||||
power->setIntervalFromNow(0);
|
power->setIntervalFromNow(0);
|
||||||
runASAP = true;
|
runASAP = true;
|
||||||
|
BaseType_t higherWake = 0;
|
||||||
},
|
},
|
||||||
CHANGE);
|
CHANGE);
|
||||||
#endif
|
#endif
|
||||||
#ifdef BATTERY_CHARGING_INV
|
|
||||||
attachInterrupt(
|
|
||||||
BATTERY_CHARGING_INV,
|
|
||||||
[]() {
|
|
||||||
power->setIntervalFromNow(0);
|
|
||||||
runASAP = true;
|
|
||||||
},
|
|
||||||
CHANGE);
|
|
||||||
#endif
|
|
||||||
enabled = found;
|
enabled = found;
|
||||||
low_voltage_counter = 0;
|
low_voltage_counter = 0;
|
||||||
|
|
||||||
@ -775,8 +759,6 @@ void Power::shutdown()
|
|||||||
if (screen) {
|
if (screen) {
|
||||||
#ifdef T_DECK_PRO
|
#ifdef T_DECK_PRO
|
||||||
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button
|
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button
|
||||||
#elif defined(USE_EINK)
|
|
||||||
screen->showSimpleBanner("Shutting Down...", 2250); // dismiss after 3 seconds to avoid the banner on the sleep screen
|
|
||||||
#else
|
#else
|
||||||
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
|
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
|
||||||
#endif
|
#endif
|
||||||
@ -1453,7 +1435,7 @@ class LipoCharger : public HasBatteryLevel
|
|||||||
/**
|
/**
|
||||||
* return true if there is an external power source detected
|
* return true if there is an external power source detected
|
||||||
*/
|
*/
|
||||||
virtual bool isVbusIn() override { return PPM->isVbusIn(); }
|
virtual bool isVbusIn() override { return PPM->getVbusVoltage() > 0; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return true if the battery is currently charging
|
* return true if the battery is currently charging
|
||||||
|
|||||||
@ -57,21 +57,21 @@ static bool isPowered()
|
|||||||
|
|
||||||
static void sdsEnter()
|
static void sdsEnter()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: SDS");
|
LOG_DEBUG("State: SDS");
|
||||||
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
|
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
|
||||||
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, false);
|
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lowBattSDSEnter()
|
static void lowBattSDSEnter()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: Lower batt SDS");
|
LOG_DEBUG("State: Lower batt SDS");
|
||||||
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, true);
|
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, true);
|
||||||
}
|
}
|
||||||
extern Power *power;
|
extern Power *power;
|
||||||
|
|
||||||
static void shutdownEnter()
|
static void shutdownEnter()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: SHUTDOWN");
|
LOG_DEBUG("State: SHUTDOWN");
|
||||||
shutdownAtMsec = millis();
|
shutdownAtMsec = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ static uint32_t secsSlept;
|
|||||||
|
|
||||||
static void lsEnter()
|
static void lsEnter()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("lsEnter begin, ls_secs=%u", config.power.ls_secs);
|
LOG_INFO("lsEnter begin, ls_secs=%u", config.power.ls_secs);
|
||||||
if (screen)
|
if (screen)
|
||||||
screen->setOn(false);
|
screen->setOn(false);
|
||||||
secsSlept = 0; // How long have we been sleeping this time
|
secsSlept = 0; // How long have we been sleeping this time
|
||||||
@ -155,12 +155,12 @@ static void lsIdle()
|
|||||||
|
|
||||||
static void lsExit()
|
static void lsExit()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: lsExit");
|
LOG_INFO("Exit state: LS");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nbEnter()
|
static void nbEnter()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: nbEnter");
|
LOG_DEBUG("State: NB");
|
||||||
if (screen)
|
if (screen)
|
||||||
screen->setOn(false);
|
screen->setOn(false);
|
||||||
#ifdef ARCH_ESP32
|
#ifdef ARCH_ESP32
|
||||||
@ -173,7 +173,6 @@ static void nbEnter()
|
|||||||
|
|
||||||
static void darkEnter()
|
static void darkEnter()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: darkEnter");
|
|
||||||
setBluetoothEnable(true);
|
setBluetoothEnable(true);
|
||||||
if (screen)
|
if (screen)
|
||||||
screen->setOn(false);
|
screen->setOn(false);
|
||||||
@ -181,7 +180,7 @@ static void darkEnter()
|
|||||||
|
|
||||||
static void serialEnter()
|
static void serialEnter()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: serialEnter");
|
LOG_DEBUG("State: SERIAL");
|
||||||
setBluetoothEnable(false);
|
setBluetoothEnable(false);
|
||||||
if (screen) {
|
if (screen) {
|
||||||
screen->setOn(true);
|
screen->setOn(true);
|
||||||
@ -190,14 +189,13 @@ static void serialEnter()
|
|||||||
|
|
||||||
static void serialExit()
|
static void serialExit()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: serialExit");
|
|
||||||
// Turn bluetooth back on when we leave serial stream API
|
// Turn bluetooth back on when we leave serial stream API
|
||||||
setBluetoothEnable(true);
|
setBluetoothEnable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void powerEnter()
|
static void powerEnter()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: powerEnter");
|
// LOG_DEBUG("State: POWER");
|
||||||
if (!isPowered()) {
|
if (!isPowered()) {
|
||||||
// If we got here, we are in the wrong state - we should be in powered, let that state handle things
|
// If we got here, we are in the wrong state - we should be in powered, let that state handle things
|
||||||
LOG_INFO("Loss of power in Powered");
|
LOG_INFO("Loss of power in Powered");
|
||||||
@ -212,7 +210,6 @@ static void powerEnter()
|
|||||||
|
|
||||||
static void powerIdle()
|
static void powerIdle()
|
||||||
{
|
{
|
||||||
// LOG_POWERFSM("State: powerIdle"); // very chatty
|
|
||||||
if (!isPowered()) {
|
if (!isPowered()) {
|
||||||
// If we got here, we are in the wrong state
|
// If we got here, we are in the wrong state
|
||||||
LOG_INFO("Loss of power in Powered");
|
LOG_INFO("Loss of power in Powered");
|
||||||
@ -222,13 +219,14 @@ static void powerIdle()
|
|||||||
|
|
||||||
static void powerExit()
|
static void powerExit()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: powerExit");
|
if (screen)
|
||||||
|
screen->setOn(true);
|
||||||
setBluetoothEnable(true);
|
setBluetoothEnable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onEnter()
|
static void onEnter()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: onEnter");
|
LOG_DEBUG("State: ON");
|
||||||
if (screen)
|
if (screen)
|
||||||
screen->setOn(true);
|
screen->setOn(true);
|
||||||
setBluetoothEnable(true);
|
setBluetoothEnable(true);
|
||||||
@ -236,7 +234,6 @@ static void onEnter()
|
|||||||
|
|
||||||
static void onIdle()
|
static void onIdle()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: onIdle");
|
|
||||||
if (isPowered()) {
|
if (isPowered()) {
|
||||||
// If we got here, we are in the wrong state - we should be in powered, let that state handle things
|
// If we got here, we are in the wrong state - we should be in powered, let that state handle things
|
||||||
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
||||||
@ -245,7 +242,7 @@ static void onIdle()
|
|||||||
|
|
||||||
static void bootEnter()
|
static void bootEnter()
|
||||||
{
|
{
|
||||||
LOG_POWERFSM("State: bootEnter");
|
LOG_DEBUG("State: BOOT");
|
||||||
}
|
}
|
||||||
|
|
||||||
State stateSHUTDOWN(shutdownEnter, NULL, NULL, "SHUTDOWN");
|
State stateSHUTDOWN(shutdownEnter, NULL, NULL, "SHUTDOWN");
|
||||||
@ -322,6 +319,11 @@ void PowerFSM_setup()
|
|||||||
// if any packet destined for phone arrives, turn on bluetooth at least
|
// if any packet destined for phone arrives, turn on bluetooth at least
|
||||||
powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone");
|
powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone");
|
||||||
|
|
||||||
|
// Removed 2.7: we don't show the nodes individually for every node on the screen anymore
|
||||||
|
// powerFSM.add_transition(&stateNB, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
|
||||||
|
// powerFSM.add_transition(&stateDARK, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
|
||||||
|
// powerFSM.add_transition(&stateON, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
|
||||||
|
|
||||||
// Show the received text message
|
// Show the received text message
|
||||||
powerFSM.add_transition(&stateLS, &stateON, EVENT_RECEIVED_MSG, NULL, "Received text");
|
powerFSM.add_transition(&stateLS, &stateON, EVENT_RECEIVED_MSG, NULL, "Received text");
|
||||||
powerFSM.add_transition(&stateNB, &stateON, EVENT_RECEIVED_MSG, NULL, "Received text");
|
powerFSM.add_transition(&stateNB, &stateON, EVENT_RECEIVED_MSG, NULL, "Received text");
|
||||||
@ -370,7 +372,7 @@ void PowerFSM_setup()
|
|||||||
// Don't add power saving transitions if we are a power saving tracker or sensor or have Wifi enabled. Sleep will be initiated
|
// Don't add power saving transitions if we are a power saving tracker or sensor or have Wifi enabled. Sleep will be initiated
|
||||||
// through the modules
|
// through the modules
|
||||||
|
|
||||||
#if HAS_WIFI && !defined(MESHTASTIC_EXCLUDE_WIFI)
|
#if HAS_WIFI || !defined(MESHTASTIC_EXCLUDE_WIFI)
|
||||||
bool isTrackerOrSensor = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
|
bool isTrackerOrSensor = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
|
||||||
config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER ||
|
config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER ||
|
||||||
config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR;
|
config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR;
|
||||||
|
|||||||
@ -2,12 +2,6 @@
|
|||||||
|
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
|
||||||
#ifdef PowerFSMDebug
|
|
||||||
#define LOG_POWERFSM(...) LOG_DEBUG(__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define LOG_POWERFSM(...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// See sw-design.md for documentation
|
// See sw-design.md for documentation
|
||||||
|
|
||||||
#define EVENT_PRESS 1
|
#define EVENT_PRESS 1
|
||||||
|
|||||||
@ -16,7 +16,6 @@ struct ToneDuration {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Some common frequencies.
|
// Some common frequencies.
|
||||||
#define NOTE_SILENT 1
|
|
||||||
#define NOTE_C3 131
|
#define NOTE_C3 131
|
||||||
#define NOTE_CS3 139
|
#define NOTE_CS3 139
|
||||||
#define NOTE_D3 147
|
#define NOTE_D3 147
|
||||||
@ -30,16 +29,11 @@ struct ToneDuration {
|
|||||||
#define NOTE_AS3 233
|
#define NOTE_AS3 233
|
||||||
#define NOTE_B3 247
|
#define NOTE_B3 247
|
||||||
#define NOTE_CS4 277
|
#define NOTE_CS4 277
|
||||||
#define NOTE_B4 494
|
|
||||||
#define NOTE_F5 698
|
|
||||||
#define NOTE_G6 1568
|
|
||||||
#define NOTE_E7 2637
|
|
||||||
|
|
||||||
const int DURATION_1_16 = 62; // 1/16 note
|
|
||||||
const int DURATION_1_8 = 125; // 1/8 note
|
const int DURATION_1_8 = 125; // 1/8 note
|
||||||
const int DURATION_1_4 = 250; // 1/4 note
|
const int DURATION_1_4 = 250; // 1/4 note
|
||||||
const int DURATION_1_2 = 500; // 1/2 note
|
const int DURATION_1_2 = 500; // 1/2 note
|
||||||
const int DURATION_3_4 = 750; // 3/4 note
|
const int DURATION_3_4 = 750; // 1/4 note
|
||||||
const int DURATION_1_1 = 1000; // 1/1 note
|
const int DURATION_1_1 = 1000; // 1/1 note
|
||||||
|
|
||||||
void playTones(const ToneDuration *tone_durations, int size)
|
void playTones(const ToneDuration *tone_durations, int size)
|
||||||
@ -77,24 +71,13 @@ void playLongBeep()
|
|||||||
|
|
||||||
void playGPSEnableBeep()
|
void playGPSEnableBeep()
|
||||||
{
|
{
|
||||||
#if defined(R1_NEO) || defined(MUZI_BASE)
|
|
||||||
ToneDuration melody[] = {
|
|
||||||
{NOTE_F5, DURATION_1_2}, {NOTE_G6, DURATION_1_8}, {NOTE_E7, DURATION_1_4}, {NOTE_SILENT, DURATION_1_2}};
|
|
||||||
#else
|
|
||||||
ToneDuration melody[] = {{NOTE_C3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_CS4, DURATION_1_4}};
|
ToneDuration melody[] = {{NOTE_C3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_CS4, DURATION_1_4}};
|
||||||
#endif
|
|
||||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||||
}
|
}
|
||||||
|
|
||||||
void playGPSDisableBeep()
|
void playGPSDisableBeep()
|
||||||
{
|
{
|
||||||
#if defined(R1_NEO) || defined(MUZI_BASE)
|
|
||||||
ToneDuration melody[] = {{NOTE_B4, DURATION_1_16}, {NOTE_B4, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8},
|
|
||||||
{NOTE_F3, DURATION_1_16}, {NOTE_F3, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8},
|
|
||||||
{NOTE_C3, DURATION_1_1}, {NOTE_SILENT, DURATION_1_1}};
|
|
||||||
#else
|
|
||||||
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_C3, DURATION_1_4}};
|
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_C3, DURATION_1_4}};
|
||||||
#endif
|
|
||||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,29 +36,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
/* Offer chance for variant-specific defines */
|
/* Offer chance for variant-specific defines */
|
||||||
#include "variant.h"
|
#include "variant.h"
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Display feature overrides
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Allow build environments to opt-in explicitly to the E-Ink UI stack while
|
|
||||||
// keeping headless targets slim by default. Existing variants that already
|
|
||||||
// define USE_EINK continue to work without additional flags.
|
|
||||||
#ifndef MESHTASTIC_USE_EINK_UI
|
|
||||||
#ifdef USE_EINK
|
|
||||||
#define MESHTASTIC_USE_EINK_UI 1
|
|
||||||
#else
|
|
||||||
#define MESHTASTIC_USE_EINK_UI 0
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if MESHTASTIC_USE_EINK_UI
|
|
||||||
#ifndef USE_EINK
|
|
||||||
#define USE_EINK
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#undef USE_EINK
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Version
|
// Version
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@ -273,9 +250,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
// Touchscreen
|
// Touchscreen
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
#define FT6336U_ADDR 0x48
|
#define FT6336U_ADDR 0x48
|
||||||
#define CST328_ADDR 0x1A // same address as CST226SE
|
#define CST328_ADDR 0x1A
|
||||||
#define CHSC6X_ADDR 0x2E
|
#define CHSC6X_ADDR 0x2E
|
||||||
#define CST226SE_ADDR_ALT 0x5A
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
|
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
|
||||||
@ -394,9 +370,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#ifndef HAS_BLUETOOTH
|
#ifndef HAS_BLUETOOTH
|
||||||
#define HAS_BLUETOOTH 0
|
#define HAS_BLUETOOTH 0
|
||||||
#endif
|
#endif
|
||||||
#ifndef USE_TFTDISPLAY
|
|
||||||
#define USE_TFTDISPLAY 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef HW_VENDOR
|
#ifndef HW_VENDOR
|
||||||
#error HW_VENDOR must be defined
|
#error HW_VENDOR must be defined
|
||||||
@ -423,13 +396,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define HAS_RGB_LED
|
#define HAS_RGB_LED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LED_STATE_OFF
|
|
||||||
#define LED_STATE_OFF 0
|
|
||||||
#endif
|
|
||||||
#ifndef LED_STATE_ON
|
|
||||||
#define LED_STATE_ON 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// default mapping of pins
|
// default mapping of pins
|
||||||
#if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN)
|
#if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN)
|
||||||
#define ALT_BUTTON_PIN PIN_BUTTON2
|
#define ALT_BUTTON_PIN PIN_BUTTON2
|
||||||
|
|||||||
@ -85,8 +85,7 @@ class ScanI2C
|
|||||||
DRV2605,
|
DRV2605,
|
||||||
BH1750,
|
BH1750,
|
||||||
DA217,
|
DA217,
|
||||||
CHSC6X,
|
CHSC6X
|
||||||
CST226SE
|
|
||||||
} DeviceType;
|
} DeviceType;
|
||||||
|
|
||||||
// typedef uint8_t DeviceAddress;
|
// typedef uint8_t DeviceAddress;
|
||||||
|
|||||||
@ -499,18 +499,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
|
||||||
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address);
|
||||||
case CST328_ADDR:
|
SCAN_SIMPLE_CASE(CST328_ADDR, CST328, "CST328", (uint8_t)addr.address);
|
||||||
// Do we have the CST328 or the CST226SE
|
|
||||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xAB), 1);
|
|
||||||
if (registerValue == 0xA9) {
|
|
||||||
type = CST226SE;
|
|
||||||
logFoundDevice("CST226SE", (uint8_t)addr.address);
|
|
||||||
} else {
|
|
||||||
type = CST328;
|
|
||||||
logFoundDevice("CST328", (uint8_t)addr.address);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
SCAN_SIMPLE_CASE(CHSC6X_ADDR, CHSC6X, "CHSC6X", (uint8_t)addr.address);
|
SCAN_SIMPLE_CASE(CHSC6X_ADDR, CHSC6X, "CHSC6X", (uint8_t)addr.address);
|
||||||
case LTR553ALS_ADDR:
|
case LTR553ALS_ADDR:
|
||||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x86), 1); // Part ID register
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x86), 1); // Part ID register
|
||||||
@ -539,12 +528,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
case MLX90614_ADDR_DEF:
|
case MLX90614_ADDR_DEF:
|
||||||
// Do we have the MLX90614 or the MPR121KB or the CST226SE
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1);
|
||||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x06), 1);
|
if (registerValue == 0x5a) {
|
||||||
if (registerValue == 0xAB) {
|
|
||||||
type = CST226SE;
|
|
||||||
logFoundDevice("CST226SE", (uint8_t)addr.address);
|
|
||||||
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1) == 0x5a) {
|
|
||||||
type = MLX90614;
|
type = MLX90614;
|
||||||
logFoundDevice("MLX90614", (uint8_t)addr.address);
|
logFoundDevice("MLX90614", (uint8_t)addr.address);
|
||||||
} else {
|
} else {
|
||||||
@ -562,11 +547,6 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
|||||||
case ICM20948_ADDR: // same as BMX160_ADDR
|
case ICM20948_ADDR: // same as BMX160_ADDR
|
||||||
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR
|
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR
|
||||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
|
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
|
||||||
#ifdef HAS_ICM20948
|
|
||||||
type = ICM20948;
|
|
||||||
logFoundDevice("ICM20948", (uint8_t)addr.address);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
if (registerValue == 0xEA) {
|
if (registerValue == 0xEA) {
|
||||||
type = ICM20948;
|
type = ICM20948;
|
||||||
logFoundDevice("ICM20948", (uint8_t)addr.address);
|
logFoundDevice("ICM20948", (uint8_t)addr.address);
|
||||||
|
|||||||
@ -38,16 +38,14 @@ template <typename T, std::size_t N> std::size_t array_count(const T (&)[N])
|
|||||||
return N;
|
return N;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef GPS_SERIAL_PORT
|
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
|
||||||
#define GPS_SERIAL_PORT Serial1
|
#if defined(GPS_SERIAL_PORT)
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(ARCH_NRF52)
|
|
||||||
Uart *GPS::_serial_gps = &GPS_SERIAL_PORT;
|
|
||||||
#elif defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
|
|
||||||
HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT;
|
HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT;
|
||||||
|
#else
|
||||||
|
HardwareSerial *GPS::_serial_gps = &Serial1;
|
||||||
|
#endif
|
||||||
#elif defined(ARCH_RP2040)
|
#elif defined(ARCH_RP2040)
|
||||||
SerialUART *GPS::_serial_gps = &GPS_SERIAL_PORT;
|
SerialUART *GPS::_serial_gps = &Serial1;
|
||||||
#else
|
#else
|
||||||
HardwareSerial *GPS::_serial_gps = nullptr;
|
HardwareSerial *GPS::_serial_gps = nullptr;
|
||||||
#endif
|
#endif
|
||||||
@ -1527,7 +1525,10 @@ GPS *GPS::createGps()
|
|||||||
int8_t _rx_gpio = config.position.rx_gpio;
|
int8_t _rx_gpio = config.position.rx_gpio;
|
||||||
int8_t _tx_gpio = config.position.tx_gpio;
|
int8_t _tx_gpio = config.position.tx_gpio;
|
||||||
int8_t _en_gpio = config.position.gps_en_gpio;
|
int8_t _en_gpio = config.position.gps_en_gpio;
|
||||||
|
#if HAS_GPS && !defined(ARCH_ESP32)
|
||||||
|
_rx_gpio = 1; // We only specify GPS serial ports on ESP32. Otherwise, these are just flags.
|
||||||
|
_tx_gpio = 1;
|
||||||
|
#endif
|
||||||
#if defined(GPS_RX_PIN)
|
#if defined(GPS_RX_PIN)
|
||||||
if (!_rx_gpio)
|
if (!_rx_gpio)
|
||||||
_rx_gpio = GPS_RX_PIN;
|
_rx_gpio = GPS_RX_PIN;
|
||||||
@ -1601,28 +1602,16 @@ GPS *GPS::createGps()
|
|||||||
_serial_gps->setRxBufferSize(SERIAL_BUFFER_SIZE); // the default is 256
|
_serial_gps->setRxBufferSize(SERIAL_BUFFER_SIZE); // the default is 256
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LOG_DEBUG("Use GPIO%d for GPS RX", new_gps->rx_gpio);
|
|
||||||
LOG_DEBUG("Use GPIO%d for GPS TX", new_gps->tx_gpio);
|
|
||||||
|
|
||||||
// ESP32 has a special set of parameters vs other arduino ports
|
// ESP32 has a special set of parameters vs other arduino ports
|
||||||
#if defined(ARCH_ESP32)
|
#if defined(ARCH_ESP32)
|
||||||
|
LOG_DEBUG("Use GPIO%d for GPS RX", new_gps->rx_gpio);
|
||||||
|
LOG_DEBUG("Use GPIO%d for GPS TX", new_gps->tx_gpio);
|
||||||
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, new_gps->rx_gpio, new_gps->tx_gpio);
|
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, new_gps->rx_gpio, new_gps->tx_gpio);
|
||||||
#elif defined(ARCH_RP2040)
|
#elif defined(ARCH_RP2040)
|
||||||
_serial_gps->setPinout(new_gps->tx_gpio, new_gps->rx_gpio);
|
|
||||||
_serial_gps->setFIFOSize(256);
|
_serial_gps->setFIFOSize(256);
|
||||||
_serial_gps->begin(GPS_BAUDRATE);
|
_serial_gps->begin(GPS_BAUDRATE);
|
||||||
#elif defined(ARCH_NRF52)
|
|
||||||
_serial_gps->setPins(new_gps->rx_gpio, new_gps->tx_gpio);
|
|
||||||
_serial_gps->begin(GPS_BAUDRATE);
|
|
||||||
#elif defined(ARCH_STM32WL)
|
|
||||||
_serial_gps->setTx(new_gps->tx_gpio);
|
|
||||||
_serial_gps->setRx(new_gps->rx_gpio);
|
|
||||||
_serial_gps->begin(GPS_BAUDRATE);
|
|
||||||
#elif defined(ARCH_PORTDUINO)
|
|
||||||
// Portduino can't set the GPS pins directly.
|
|
||||||
_serial_gps->begin(GPS_BAUDRATE);
|
|
||||||
#else
|
#else
|
||||||
#error Unsupported architecture!
|
_serial_gps->begin(GPS_BAUDRATE);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return new_gps;
|
return new_gps;
|
||||||
|
|||||||
@ -194,8 +194,6 @@ class GPS : private concurrency::OSThread
|
|||||||
/** If !NULL we will use this serial port to construct our GPS */
|
/** If !NULL we will use this serial port to construct our GPS */
|
||||||
#if defined(ARCH_RP2040)
|
#if defined(ARCH_RP2040)
|
||||||
static SerialUART *_serial_gps;
|
static SerialUART *_serial_gps;
|
||||||
#elif defined(ARCH_NRF52)
|
|
||||||
static Uart *_serial_gps;
|
|
||||||
#else
|
#else
|
||||||
static HardwareSerial *_serial_gps;
|
static HardwareSerial *_serial_gps;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -112,11 +112,7 @@ RTCSetResult readFromRTC()
|
|||||||
#elif defined(RX8130CE_RTC)
|
#elif defined(RX8130CE_RTC)
|
||||||
if (rtc_found.address == RX8130CE_RTC) {
|
if (rtc_found.address == RX8130CE_RTC) {
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
#ifdef MUZI_BASE
|
|
||||||
ArtronShop_RX8130CE rtc(&Wire1);
|
|
||||||
#else
|
|
||||||
ArtronShop_RX8130CE rtc(&Wire);
|
ArtronShop_RX8130CE rtc(&Wire);
|
||||||
#endif
|
|
||||||
tm t;
|
tm t;
|
||||||
if (rtc.getTime(&t)) {
|
if (rtc.getTime(&t)) {
|
||||||
tv.tv_sec = gm_mktime(&t);
|
tv.tv_sec = gm_mktime(&t);
|
||||||
@ -249,11 +245,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
|
|||||||
}
|
}
|
||||||
#elif defined(RX8130CE_RTC)
|
#elif defined(RX8130CE_RTC)
|
||||||
if (rtc_found.address == RX8130CE_RTC) {
|
if (rtc_found.address == RX8130CE_RTC) {
|
||||||
#ifdef MUZI_BASE
|
|
||||||
ArtronShop_RX8130CE rtc(&Wire1);
|
|
||||||
#else
|
|
||||||
ArtronShop_RX8130CE rtc(&Wire);
|
ArtronShop_RX8130CE rtc(&Wire);
|
||||||
#endif
|
|
||||||
tm *t = gmtime(&tv->tv_sec);
|
tm *t = gmtime(&tv->tv_sec);
|
||||||
if (rtc.setTime(*t)) {
|
if (rtc.setTime(*t)) {
|
||||||
LOG_DEBUG("RX8130CE setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1,
|
LOG_DEBUG("RX8130CE setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1,
|
||||||
|
|||||||
@ -69,11 +69,7 @@ using graphics::Emote;
|
|||||||
using graphics::emotes;
|
using graphics::emotes;
|
||||||
using graphics::numEmotes;
|
using graphics::numEmotes;
|
||||||
|
|
||||||
#if USE_TFTDISPLAY
|
|
||||||
extern uint16_t TFT_MESH;
|
extern uint16_t TFT_MESH;
|
||||||
#else
|
|
||||||
uint16_t TFT_MESH = COLOR565(0x67, 0xEA, 0x94);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
||||||
#include "mesh/wifi/WiFiAPClient.h"
|
#include "mesh/wifi/WiFiAPClient.h"
|
||||||
@ -230,9 +226,24 @@ void Screen::showTextInput(const char *header, const char *initialText, uint32_t
|
|||||||
{
|
{
|
||||||
LOG_INFO("showTextInput called with header='%s', durationMs=%d", header ? header : "NULL", durationMs);
|
LOG_INFO("showTextInput called with header='%s', durationMs=%d", header ? header : "NULL", durationMs);
|
||||||
|
|
||||||
// Start OnScreenKeyboardModule session (non-touch variant)
|
if (NotificationRenderer::virtualKeyboard) {
|
||||||
OnScreenKeyboardModule::instance().start(header, initialText, durationMs, textCallback);
|
delete NotificationRenderer::virtualKeyboard;
|
||||||
|
NotificationRenderer::virtualKeyboard = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationRenderer::textInputCallback = nullptr;
|
||||||
|
|
||||||
|
NotificationRenderer::virtualKeyboard = new VirtualKeyboard();
|
||||||
|
if (header) {
|
||||||
|
NotificationRenderer::virtualKeyboard->setHeader(header);
|
||||||
|
}
|
||||||
|
if (initialText) {
|
||||||
|
NotificationRenderer::virtualKeyboard->setInputText(initialText);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up callback with safer cleanup mechanism
|
||||||
NotificationRenderer::textInputCallback = textCallback;
|
NotificationRenderer::textInputCallback = textCallback;
|
||||||
|
NotificationRenderer::virtualKeyboard->setCallback([textCallback](const std::string &text) { textCallback(text); });
|
||||||
|
|
||||||
// Store the message and set the expiration timestamp (use same pattern as other notifications)
|
// Store the message and set the expiration timestamp (use same pattern as other notifications)
|
||||||
strncpy(NotificationRenderer::alertBannerMessage, header ? header : "Text Input", 255);
|
strncpy(NotificationRenderer::alertBannerMessage, header ? header : "Text Input", 255);
|
||||||
@ -313,7 +324,7 @@ static int8_t prevFrame = -1;
|
|||||||
// Combined dynamic node list frame cycling through LastHeard, HopSignal, and Distance modes
|
// Combined dynamic node list frame cycling through LastHeard, HopSignal, and Distance modes
|
||||||
// Uses a single frame and changes data every few seconds (E-Ink variant is separate)
|
// Uses a single frame and changes data every few seconds (E-Ink variant is separate)
|
||||||
|
|
||||||
#if defined(ESP_PLATFORM) && (defined(USE_ST7789) || defined(USE_ST7796))
|
#if defined(ESP_PLATFORM) && defined(USE_ST7789)
|
||||||
SPIClass SPI1(HSPI);
|
SPIClass SPI1(HSPI);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -345,13 +356,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
|||||||
#else
|
#else
|
||||||
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
|
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
|
||||||
#endif
|
#endif
|
||||||
#elif defined(USE_ST7796)
|
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
|
||||||
#ifdef ESP_PLATFORM
|
|
||||||
dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT, ST7796_SDA,
|
|
||||||
ST7796_MISO, ST7796_SCK, TFT_SPI_FREQUENCY);
|
|
||||||
#else
|
|
||||||
dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
|
|
||||||
#endif
|
|
||||||
#elif defined(USE_SSD1306)
|
#elif defined(USE_SSD1306)
|
||||||
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
||||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||||
@ -364,7 +369,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
|||||||
LOG_INFO("SSD1306 init success");
|
LOG_INFO("SSD1306 init success");
|
||||||
}
|
}
|
||||||
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \
|
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \
|
||||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR)
|
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||||
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
|
||||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||||
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
|
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
|
||||||
@ -394,12 +399,6 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
|||||||
isAUTOOled = true;
|
isAUTOOled = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(USE_ST7789)
|
|
||||||
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
|
|
||||||
#elif defined(USE_ST7796)
|
|
||||||
static_cast<ST7796Spi *>(dispdev)->setRGB(TFT_MESH);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ui = new OLEDDisplayUi(dispdev);
|
ui = new OLEDDisplayUi(dispdev);
|
||||||
cmdQueue.setReader(this);
|
cmdQueue.setReader(this);
|
||||||
}
|
}
|
||||||
@ -436,14 +435,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
|||||||
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
PMU->enablePowerOutput(XPOWERS_ALDO2);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(MUZI_BASE)
|
|
||||||
dispdev->init();
|
|
||||||
dispdev->setBrightness(brightness);
|
|
||||||
dispdev->flipScreenVertically();
|
|
||||||
dispdev->resetDisplay();
|
|
||||||
digitalWrite(SCREEN_12V_ENABLE, HIGH);
|
|
||||||
delay(100);
|
|
||||||
#endif
|
|
||||||
#if !ARCH_PORTDUINO
|
#if !ARCH_PORTDUINO
|
||||||
dispdev->displayOn();
|
dispdev->displayOn();
|
||||||
#endif
|
#endif
|
||||||
@ -475,15 +466,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
|||||||
pinMode(VTFT_LEDA, OUTPUT);
|
pinMode(VTFT_LEDA, OUTPUT);
|
||||||
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
|
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
#ifdef USE_ST7796
|
|
||||||
ui->init();
|
|
||||||
#ifdef ESP_PLATFORM
|
|
||||||
analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT);
|
|
||||||
#else
|
|
||||||
pinMode(VTFT_LEDA, OUTPUT);
|
|
||||||
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
|
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
enabled = true;
|
enabled = true;
|
||||||
setInterval(0); // Draw ASAP
|
setInterval(0); // Draw ASAP
|
||||||
@ -502,10 +484,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
dispdev->displayOff();
|
dispdev->displayOff();
|
||||||
|
|
||||||
#ifdef SCREEN_12V_ENABLE
|
|
||||||
digitalWrite(SCREEN_12V_ENABLE, LOW);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_ST7789
|
#ifdef USE_ST7789
|
||||||
SPI1.end();
|
SPI1.end();
|
||||||
#if defined(ARCH_ESP32)
|
#if defined(ARCH_ESP32)
|
||||||
@ -522,21 +500,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
|||||||
nrf_gpio_cfg_default(ST7789_NSS);
|
nrf_gpio_cfg_default(ST7789_NSS);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ST7796
|
|
||||||
SPI1.end();
|
|
||||||
#if defined(ARCH_ESP32)
|
|
||||||
pinMode(VTFT_LEDA, OUTPUT);
|
|
||||||
digitalWrite(VTFT_LEDA, LOW);
|
|
||||||
pinMode(ST7796_RESET, ANALOG);
|
|
||||||
pinMode(ST7796_RS, ANALOG);
|
|
||||||
pinMode(ST7796_NSS, ANALOG);
|
|
||||||
#else
|
|
||||||
nrf_gpio_cfg_default(VTFT_LEDA);
|
|
||||||
nrf_gpio_cfg_default(ST7796_RESET);
|
|
||||||
nrf_gpio_cfg_default(ST7796_RS);
|
|
||||||
nrf_gpio_cfg_default(ST7796_NSS);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef T_WATCH_S3
|
#ifdef T_WATCH_S3
|
||||||
PMU->disablePowerOutput(XPOWERS_ALDO2);
|
PMU->disablePowerOutput(XPOWERS_ALDO2);
|
||||||
@ -571,7 +534,7 @@ void Screen::setup()
|
|||||||
static_cast<AutoOLEDWire *>(dispdev)->setDetected(model);
|
static_cast<AutoOLEDWire *>(dispdev)->setDetected(model);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(USE_SH1107_128_64) || defined(USE_SH1107)
|
#ifdef USE_SH1107_128_64
|
||||||
static_cast<SH1106Wire *>(dispdev)->setSubtype(7);
|
static_cast<SH1106Wire *>(dispdev)->setSubtype(7);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -579,13 +542,6 @@ void Screen::setup()
|
|||||||
// Apply custom RGB color (e.g. Heltec T114/T190)
|
// Apply custom RGB color (e.g. Heltec T114/T190)
|
||||||
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
|
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
|
||||||
#endif
|
#endif
|
||||||
#if defined(MUZI_BASE)
|
|
||||||
dispdev->delayPoweron = true;
|
|
||||||
#endif
|
|
||||||
#if defined(USE_ST7796) && defined(TFT_MESH)
|
|
||||||
// Custom text color, if defined in variant.h
|
|
||||||
static_cast<ST7796Spi *>(dispdev)->setRGB(TFT_MESH);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// === Initialize display and UI system ===
|
// === Initialize display and UI system ===
|
||||||
ui->init();
|
ui->init();
|
||||||
@ -645,12 +601,10 @@ void Screen::setup()
|
|||||||
#else
|
#else
|
||||||
if (!config.display.flip_screen) {
|
if (!config.display.flip_screen) {
|
||||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
||||||
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR)
|
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
|
||||||
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
|
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
|
||||||
#elif defined(USE_ST7789)
|
#elif defined(USE_ST7789)
|
||||||
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
|
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
|
||||||
#elif defined(USE_ST7796)
|
|
||||||
static_cast<ST7796Spi *>(dispdev)->mirrorScreen();
|
|
||||||
#elif !defined(M5STACK_UNITC6L)
|
#elif !defined(M5STACK_UNITC6L)
|
||||||
dispdev->flipScreenVertically();
|
dispdev->flipScreenVertically();
|
||||||
#endif
|
#endif
|
||||||
@ -683,7 +637,7 @@ void Screen::setup()
|
|||||||
touchScreenImpl1->init();
|
touchScreenImpl1->init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#elif HAS_TOUCHSCREEN && !defined(USE_EINK) && !HAS_CST226SE
|
#elif HAS_TOUCHSCREEN && !defined(USE_EINK)
|
||||||
touchScreenImpl1 =
|
touchScreenImpl1 =
|
||||||
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
|
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
|
||||||
touchScreenImpl1->init();
|
touchScreenImpl1->init();
|
||||||
@ -1498,14 +1452,14 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
// Incoming message
|
// Incoming message
|
||||||
devicestate.has_rx_text_message = true; // Needed to include the message frame
|
devicestate.has_rx_text_message = true; // Needed to include the message frame
|
||||||
hasUnreadMessage = true; // Enables mail icon in the header
|
hasUnreadMessage = true; // Enables mail icon in the header
|
||||||
setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view (no-op during text_input)
|
setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view
|
||||||
|
|
||||||
// Only wake/force display if the configuration allows it
|
// Only wake/force display if the configuration allows it
|
||||||
if (shouldWakeOnReceivedMessage()) {
|
if (shouldWakeOnReceivedMessage()) {
|
||||||
setOn(true); // Wake up the screen first
|
setOn(true); // Wake up the screen first
|
||||||
forceDisplay(); // Forces screen redraw
|
forceDisplay(); // Forces screen redraw
|
||||||
}
|
}
|
||||||
// === Prepare banner/popup content ===
|
// === Prepare banner content ===
|
||||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
||||||
const meshtastic_Channel channel =
|
const meshtastic_Channel channel =
|
||||||
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
|
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
|
||||||
@ -1529,52 +1483,6 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
|
|
||||||
// Unlike generic messages, alerts (when enabled via the ext notif module) ignore any
|
// Unlike generic messages, alerts (when enabled via the ext notif module) ignore any
|
||||||
// 'mute' preferences set to any specific node or channel.
|
// 'mute' preferences set to any specific node or channel.
|
||||||
// If on-screen keyboard is active, show a transient popup over keyboard instead of interrupting it
|
|
||||||
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
|
|
||||||
// Wake and force redraw so popup is visible immediately
|
|
||||||
if (shouldWakeOnReceivedMessage()) {
|
|
||||||
setOn(true);
|
|
||||||
forceDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build popup: title = message source name, content = message text (sanitized)
|
|
||||||
// Title
|
|
||||||
char titleBuf[64] = {0};
|
|
||||||
if (longName && longName[0]) {
|
|
||||||
// Sanitize sender name
|
|
||||||
std::string t = sanitizeString(longName);
|
|
||||||
strncpy(titleBuf, t.c_str(), sizeof(titleBuf) - 1);
|
|
||||||
} else {
|
|
||||||
strncpy(titleBuf, "Message", sizeof(titleBuf) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Content: payload bytes may not be null-terminated, remove ASCII_BELL and sanitize
|
|
||||||
char content[256] = {0};
|
|
||||||
{
|
|
||||||
std::string raw;
|
|
||||||
raw.reserve(packet->decoded.payload.size);
|
|
||||||
for (size_t i = 0; i < packet->decoded.payload.size; ++i) {
|
|
||||||
char c = msgRaw[i];
|
|
||||||
if (c == ASCII_BELL)
|
|
||||||
continue; // strip bell
|
|
||||||
raw.push_back(c);
|
|
||||||
}
|
|
||||||
std::string sanitized = sanitizeString(raw);
|
|
||||||
strncpy(content, sanitized.c_str(), sizeof(content) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationRenderer::showKeyboardMessagePopupWithTitle(titleBuf, content, 3000);
|
|
||||||
|
|
||||||
// Maintain existing buzzer behavior on M5 if applicable
|
|
||||||
#if defined(M5STACK_UNITC6L)
|
|
||||||
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
|
|
||||||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
|
|
||||||
(!isBroadcast(packet->to) && isToUs(packet))) {
|
|
||||||
playLongBeep();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
// No keyboard active: use regular banner flow, respecting mute settings
|
|
||||||
if (isAlert) {
|
if (isAlert) {
|
||||||
if (longName && longName[0]) {
|
if (longName && longName[0]) {
|
||||||
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
|
||||||
@ -1589,6 +1497,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
#else
|
#else
|
||||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
strcpy(banner, "New Message");
|
strcpy(banner, "New Message");
|
||||||
}
|
}
|
||||||
@ -1609,7 +1518,6 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1641,7 +1549,6 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
|
|||||||
|
|
||||||
int Screen::handleInputEvent(const InputEvent *event)
|
int Screen::handleInputEvent(const InputEvent *event)
|
||||||
{
|
{
|
||||||
LOG_INPUT("Screen Input event %u! kb %u", event->inputEvent, event->kbchar);
|
|
||||||
if (!screenOn)
|
if (!screenOn)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -1689,12 +1596,6 @@ int Screen::handleInputEvent(const InputEvent *event)
|
|||||||
showPrevFrame();
|
showPrevFrame();
|
||||||
} else if (event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS) {
|
} else if (event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||||
showNextFrame();
|
showNextFrame();
|
||||||
} else if (event->inputEvent == INPUT_BROKER_UP_LONG) {
|
|
||||||
// Long press up button for fast frame switching
|
|
||||||
showPrevFrame();
|
|
||||||
} else if (event->inputEvent == INPUT_BROKER_DOWN_LONG) {
|
|
||||||
// Long press down button for fast frame switching
|
|
||||||
showNextFrame();
|
|
||||||
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
|
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
|
||||||
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
||||||
menuHandler::homeBaseMenu();
|
menuHandler::homeBaseMenu();
|
||||||
|
|||||||
@ -83,8 +83,6 @@ class Screen
|
|||||||
#include <ST7789Spi.h>
|
#include <ST7789Spi.h>
|
||||||
#elif defined(USE_SPISSD1306)
|
#elif defined(USE_SPISSD1306)
|
||||||
#include <SSD1306Spi.h>
|
#include <SSD1306Spi.h>
|
||||||
#elif defined(USE_ST7796)
|
|
||||||
#include <ST7796Spi.h>
|
|
||||||
#else
|
#else
|
||||||
// the SH1106/SSD1306 variant is auto-detected
|
// the SH1106/SSD1306 variant is auto-detected
|
||||||
#include <AutoOLEDWire.h>
|
#include <AutoOLEDWire.h>
|
||||||
@ -251,8 +249,6 @@ class Screen : public concurrency::OSThread
|
|||||||
|
|
||||||
bool isOverlayBannerShowing();
|
bool isOverlayBannerShowing();
|
||||||
|
|
||||||
bool isScreenOn() { return screenOn; }
|
|
||||||
|
|
||||||
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
|
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
|
||||||
// FIXME: Needs refactoring and getMacAddr needs to be moved to a utility class
|
// FIXME: Needs refactoring and getMacAddr needs to be moved to a utility class
|
||||||
char ourId[5];
|
char ourId[5];
|
||||||
|
|||||||
@ -73,8 +73,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || \
|
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)) && \
|
||||||
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
|
|
||||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||||
// The screen is bigger so use bigger fonts
|
// The screen is bigger so use bigger fonts
|
||||||
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
|
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
|
||||||
|
|||||||
@ -17,12 +17,6 @@ namespace graphics
|
|||||||
|
|
||||||
void determineResolution(int16_t screenheight, int16_t screenwidth)
|
void determineResolution(int16_t screenheight, int16_t screenwidth)
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef FORCE_LOW_RES
|
|
||||||
isHighResolution = false;
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (screenwidth > 128) {
|
if (screenwidth > 128) {
|
||||||
isHighResolution = true;
|
isHighResolution = true;
|
||||||
}
|
}
|
||||||
@ -30,6 +24,11 @@ void determineResolution(int16_t screenheight, int16_t screenwidth)
|
|||||||
if (screenwidth > 128 && screenheight <= 64) {
|
if (screenwidth > 128 && screenheight <= 64) {
|
||||||
isHighResolution = false;
|
isHighResolution = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special case for Heltec Wireless Tracker v1.1
|
||||||
|
if (screenwidth == 160 && screenheight == 80) {
|
||||||
|
isHighResolution = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Shared External State ===
|
// === Shared External State ===
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#if USE_TFTDISPLAY
|
|
||||||
|
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
#include "platform/portduino/PortduinoGlue.h"
|
#include "platform/portduino/PortduinoGlue.h"
|
||||||
@ -124,11 +123,6 @@ static void rak14014_tpIntHandle(void)
|
|||||||
_rak14014_touch_int = true;
|
_rak14014_touch_int = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(HACKADAY_COMMUNICATOR)
|
|
||||||
#include <Arduino_GFX_Library.h>
|
|
||||||
Arduino_DataBus *bus = nullptr;
|
|
||||||
Arduino_GFX *tft = nullptr;
|
|
||||||
|
|
||||||
#elif defined(ST72xx_DE)
|
#elif defined(ST72xx_DE)
|
||||||
#include <LovyanGFX.hpp>
|
#include <LovyanGFX.hpp>
|
||||||
#include <TCA9534.h>
|
#include <TCA9534.h>
|
||||||
@ -447,8 +441,7 @@ class TOUCH_CHSC6X : public ITouch
|
|||||||
_cfg.y_max = 320;
|
_cfg.y_max = 320;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool init(void) override
|
bool init(void) override {
|
||||||
{
|
|
||||||
if(chsc6xTouch==nullptr) {
|
if(chsc6xTouch==nullptr) {
|
||||||
chsc6xTouch=new chsc6x(&Wire1,TOUCH_SDA_PIN,TOUCH_SCL_PIN,TOUCH_INT_PIN,TOUCH_RST_PIN);
|
chsc6xTouch=new chsc6x(&Wire1,TOUCH_SDA_PIN,TOUCH_SCL_PIN,TOUCH_INT_PIN,TOUCH_RST_PIN);
|
||||||
}
|
}
|
||||||
@ -456,8 +449,7 @@ class TOUCH_CHSC6X : public ITouch
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint_fast8_t getTouchRaw(touch_point_t *tp, uint_fast8_t count) override
|
uint_fast8_t getTouchRaw(touch_point_t* tp, uint_fast8_t count) override {
|
||||||
{
|
|
||||||
uint16_t raw_x,raw_y;
|
uint16_t raw_x,raw_y;
|
||||||
if (chsc6xTouch->chsc6x_read_touch_info(&raw_x, &raw_y)==0) {
|
if (chsc6xTouch->chsc6x_read_touch_info(&raw_x, &raw_y)==0) {
|
||||||
tp[0].x = 320-1-raw_y;
|
tp[0].x = 320-1-raw_y;
|
||||||
@ -472,12 +464,11 @@ class TOUCH_CHSC6X : public ITouch
|
|||||||
|
|
||||||
void wakeup(void) override {};
|
void wakeup(void) override {};
|
||||||
void sleep(void) override {};
|
void sleep(void) override {};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
chsc6x *chsc6xTouch=nullptr;
|
chsc6x *chsc6xTouch=nullptr;
|
||||||
};
|
};
|
||||||
} // namespace v1
|
}
|
||||||
} // namespace lgfx
|
}
|
||||||
#endif
|
#endif
|
||||||
class LGFX : public lgfx::LGFX_Device
|
class LGFX : public lgfx::LGFX_Device
|
||||||
{
|
{
|
||||||
@ -1139,6 +1130,9 @@ static LGFX *tft = nullptr;
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || defined(ST7796_CS) || defined(ILI9341_DRIVER) || \
|
||||||
|
defined(ILI9342_DRIVER) || defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST72xx_DE) || \
|
||||||
|
(ARCH_PORTDUINO && HAS_SCREEN != 0)
|
||||||
#include "SPILock.h"
|
#include "SPILock.h"
|
||||||
#include "TFTDisplay.h"
|
#include "TFTDisplay.h"
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
@ -1274,15 +1268,12 @@ void TFTDisplay::display(bool fromBlank)
|
|||||||
x_LastPixelUpdate = x;
|
x_LastPixelUpdate = x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if defined(HACKADAY_COMMUNICATOR)
|
|
||||||
tft->draw16bitBeRGBBitmap(x_FirstPixelUpdate, y, &linePixelBuffer[x_FirstPixelUpdate],
|
|
||||||
(x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1);
|
|
||||||
#else
|
|
||||||
// Step 4: Send the changed pixels on this line to the screen as a single block transfer.
|
// Step 4: Send the changed pixels on this line to the screen as a single block transfer.
|
||||||
// This function accepts pixel data MSB first so it can dump the memory straight out the SPI port.
|
// This function accepts pixel data MSB first so it can dump the memory straight out the SPI port.
|
||||||
tft->pushRect(x_FirstPixelUpdate, y, (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1,
|
tft->pushRect(x_FirstPixelUpdate, y, (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1,
|
||||||
&linePixelBuffer[x_FirstPixelUpdate]);
|
&linePixelBuffer[x_FirstPixelUpdate]);
|
||||||
#endif
|
|
||||||
somethingChanged = true;
|
somethingChanged = true;
|
||||||
}
|
}
|
||||||
y++;
|
y++;
|
||||||
@ -1346,8 +1337,6 @@ void TFTDisplay::sendCommand(uint8_t com)
|
|||||||
display(true);
|
display(true);
|
||||||
if (portduino_config.displayBacklight.pin > 0)
|
if (portduino_config.displayBacklight.pin > 0)
|
||||||
digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON);
|
digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON);
|
||||||
#elif defined(HACKADAY_COMMUNICATOR)
|
|
||||||
tft->displayOn();
|
|
||||||
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
|
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
|
||||||
tft->wakeup();
|
tft->wakeup();
|
||||||
tft->powerSaveOff();
|
tft->powerSaveOff();
|
||||||
@ -1360,8 +1349,7 @@ void TFTDisplay::sendCommand(uint8_t com)
|
|||||||
unphone.backlight(true); // using unPhone library
|
unphone.backlight(true); // using unPhone library
|
||||||
#endif
|
#endif
|
||||||
#ifdef RAK14014
|
#ifdef RAK14014
|
||||||
#elif !defined(M5STACK) && !defined(ST7789_CS) && \
|
#elif !defined(M5STACK) && !defined(ST7789_CS) // T-Deck gets brightness set in Screen.cpp in the handleSetOn function
|
||||||
!defined(HACKADAY_COMMUNICATOR) // T-Deck gets brightness set in Screen.cpp in the handleSetOn function
|
|
||||||
tft->setBrightness(172);
|
tft->setBrightness(172);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
@ -1373,8 +1361,6 @@ void TFTDisplay::sendCommand(uint8_t com)
|
|||||||
tft->clear();
|
tft->clear();
|
||||||
if (portduino_config.displayBacklight.pin > 0)
|
if (portduino_config.displayBacklight.pin > 0)
|
||||||
digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON);
|
digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON);
|
||||||
#elif defined(HACKADAY_COMMUNICATOR)
|
|
||||||
tft->displayOff();
|
|
||||||
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
|
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
|
||||||
tft->sleep();
|
tft->sleep();
|
||||||
tft->powerSaveOn();
|
tft->powerSaveOn();
|
||||||
@ -1387,7 +1373,7 @@ void TFTDisplay::sendCommand(uint8_t com)
|
|||||||
unphone.backlight(false); // using unPhone library
|
unphone.backlight(false); // using unPhone library
|
||||||
#endif
|
#endif
|
||||||
#ifdef RAK14014
|
#ifdef RAK14014
|
||||||
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
|
#elif !defined(M5STACK)
|
||||||
tft->setBrightness(0);
|
tft->setBrightness(0);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
@ -1403,7 +1389,7 @@ void TFTDisplay::setDisplayBrightness(uint8_t _brightness)
|
|||||||
{
|
{
|
||||||
#ifdef RAK14014
|
#ifdef RAK14014
|
||||||
// todo
|
// todo
|
||||||
#elif !defined(HACKADAY_COMMUNICATOR)
|
#else
|
||||||
tft->setBrightness(_brightness);
|
tft->setBrightness(_brightness);
|
||||||
LOG_DEBUG("Brightness is set to value: %i ", _brightness);
|
LOG_DEBUG("Brightness is set to value: %i ", _brightness);
|
||||||
#endif
|
#endif
|
||||||
@ -1421,7 +1407,7 @@ bool TFTDisplay::hasTouch(void)
|
|||||||
{
|
{
|
||||||
#ifdef RAK14014
|
#ifdef RAK14014
|
||||||
return true;
|
return true;
|
||||||
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
|
#elif !defined(M5STACK)
|
||||||
return tft->touch() != nullptr;
|
return tft->touch() != nullptr;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
@ -1440,7 +1426,7 @@ bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
|
|||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
|
#elif !defined(M5STACK)
|
||||||
return tft->getTouch(x, y);
|
return tft->getTouch(x, y);
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
@ -1459,12 +1445,6 @@ bool TFTDisplay::connect()
|
|||||||
LOG_INFO("Do TFT init");
|
LOG_INFO("Do TFT init");
|
||||||
#ifdef RAK14014
|
#ifdef RAK14014
|
||||||
tft = new TFT_eSPI;
|
tft = new TFT_eSPI;
|
||||||
#elif defined(HACKADAY_COMMUNICATOR)
|
|
||||||
bus = new Arduino_ESP32SPI(TFT_DC, TFT_CS, 38 /* SCK */, 21 /* MOSI */, GFX_NOT_DEFINED /* MISO */, HSPI /* spi_num */);
|
|
||||||
tft = new Arduino_NV3007(bus, 40, 0 /* rotation */, false /* IPS */, 142 /* width */, 428 /* height */, 12 /* col offset 1 */,
|
|
||||||
0 /* row offset 1 */, 14 /* col offset 2 */, 0 /* row offset 2 */, nv3007_279_init_operations,
|
|
||||||
sizeof(nv3007_279_init_operations));
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
tft = new LGFX;
|
tft = new LGFX;
|
||||||
#endif
|
#endif
|
||||||
@ -1475,15 +1455,8 @@ bool TFTDisplay::connect()
|
|||||||
#ifdef UNPHONE
|
#ifdef UNPHONE
|
||||||
unphone.backlight(true); // using unPhone library
|
unphone.backlight(true); // using unPhone library
|
||||||
#endif
|
#endif
|
||||||
#ifdef HACKADAY_COMMUNICATOR
|
|
||||||
bool beginStatus = tft->begin();
|
|
||||||
if (beginStatus)
|
|
||||||
LOG_DEBUG("TFT Success!");
|
|
||||||
else
|
|
||||||
LOG_ERROR("TFT Fail!");
|
|
||||||
#else
|
|
||||||
tft->init();
|
tft->init();
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(M5STACK)
|
#if defined(M5STACK)
|
||||||
tft->setRotation(0);
|
tft->setRotation(0);
|
||||||
@ -1516,4 +1489,4 @@ bool TFTDisplay::connect()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_TFTDISPLAY
|
#endif
|
||||||
|
|||||||
@ -101,23 +101,3 @@ void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
|
|||||||
else
|
else
|
||||||
snprintf(timeStr, maxLength, "unknown age");
|
snprintf(timeStr, maxLength, "unknown age");
|
||||||
}
|
}
|
||||||
|
|
||||||
void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs)
|
|
||||||
{
|
|
||||||
uint32_t days = uptimeMillis / 86400000;
|
|
||||||
uint32_t hours = (uptimeMillis % 86400000) / 3600000;
|
|
||||||
uint32_t mins = (uptimeMillis % 3600000) / 60000;
|
|
||||||
uint32_t secs = (uptimeMillis % 60000) / 1000;
|
|
||||||
|
|
||||||
if (days) {
|
|
||||||
snprintf(uptimeStr, maxLength, "%s: %ud %uh", prefix, days, hours);
|
|
||||||
} else if (hours) {
|
|
||||||
snprintf(uptimeStr, maxLength, "%s: %uh %um", prefix, hours, mins);
|
|
||||||
} else if (!includeSecs) {
|
|
||||||
snprintf(uptimeStr, maxLength, "%s: %um", prefix, mins);
|
|
||||||
} else if (mins) {
|
|
||||||
snprintf(uptimeStr, maxLength, "%s: %um %us", prefix, mins, secs);
|
|
||||||
} else {
|
|
||||||
snprintf(uptimeStr, maxLength, "%s: %us", prefix, secs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -24,10 +24,3 @@ bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int
|
|||||||
* @param maxLength Maximum length of the resulting string buffer
|
* @param maxLength Maximum length of the resulting string buffer
|
||||||
*/
|
*/
|
||||||
void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength);
|
void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength);
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a compact human-readable string that only shows the largest non-zero time components.
|
|
||||||
* For example, 0 days 1 hour 2 minutes will display as "1h 2m" but 1 day 2 hours 3 minutes
|
|
||||||
* will display as "1d 2h".
|
|
||||||
*/
|
|
||||||
void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs = false);
|
|
||||||
|
|||||||
@ -506,9 +506,6 @@ void VirtualKeyboard::drawKey(OLEDDisplay *display, const VirtualKey &key, bool
|
|||||||
centeredTextY -= 1;
|
centeredTextY -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef MUZI_BASE // Correct issue with character vertical position on MUZI_BASE
|
|
||||||
centeredTextY -= 2;
|
|
||||||
#endif
|
|
||||||
display->drawString(textX, centeredTextY, keyText.c_str());
|
display->drawString(textX, centeredTextY, keyText.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,6 @@
|
|||||||
#include "gps/RTC.h"
|
#include "gps/RTC.h"
|
||||||
#include "graphics/ScreenFonts.h"
|
#include "graphics/ScreenFonts.h"
|
||||||
#include "graphics/SharedUIDisplay.h"
|
#include "graphics/SharedUIDisplay.h"
|
||||||
#include "graphics/TimeFormatters.h"
|
|
||||||
#include "graphics/images.h"
|
#include "graphics/images.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh/Channels.h"
|
#include "mesh/Channels.h"
|
||||||
@ -97,7 +96,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
|
|||||||
(storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
|
(storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
|
||||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
|
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
|
||||||
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796) || ARCH_PORTDUINO) && \
|
ARCH_PORTDUINO) && \
|
||||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12,
|
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12,
|
||||||
8, imgQuestionL1);
|
8, imgQuestionL1);
|
||||||
@ -109,8 +108,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
|
|||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
|
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \
|
||||||
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
|
|
||||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||||
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 16,
|
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 16,
|
||||||
8, imgSFL1);
|
8, imgSFL1);
|
||||||
@ -126,7 +124,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
|
|||||||
// TODO: Raspberry Pi supports more than just the one screen size
|
// TODO: Raspberry Pi supports more than just the one screen size
|
||||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
|
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
|
||||||
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796) || ARCH_PORTDUINO) && \
|
ARCH_PORTDUINO) && \
|
||||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||||
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
|
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
|
||||||
imgInfoL1);
|
imgInfoL1);
|
||||||
@ -652,7 +650,17 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
|
|||||||
|
|
||||||
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line <= 5)) { // Only show uptime if the screen can show it
|
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line <= 5)) { // Only show uptime if the screen can show it
|
||||||
char uptimeStr[32] = "";
|
char uptimeStr[32] = "";
|
||||||
getUptimeStr(millis(), "Up", uptimeStr, sizeof(uptimeStr));
|
uint32_t uptime = millis() / 1000;
|
||||||
|
uint32_t days = uptime / 86400;
|
||||||
|
uint32_t hours = (uptime % 86400) / 3600;
|
||||||
|
uint32_t mins = (uptime % 3600) / 60;
|
||||||
|
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
|
||||||
|
if (days)
|
||||||
|
snprintf(uptimeStr, sizeof(uptimeStr), " Up: %ud %uh", days, hours);
|
||||||
|
else if (hours)
|
||||||
|
snprintf(uptimeStr, sizeof(uptimeStr), " Up: %uh %um", hours, mins);
|
||||||
|
else
|
||||||
|
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
|
||||||
textWidth = display->getStringWidth(uptimeStr);
|
textWidth = display->getStringWidth(uptimeStr);
|
||||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||||
display->drawString(nameX, getTextPositions(display)[line++], uptimeStr);
|
display->drawString(nameX, getTextPositions(display)[line++], uptimeStr);
|
||||||
|
|||||||
@ -13,7 +13,6 @@
|
|||||||
#include "input/RotaryEncoderInterruptImpl1.h"
|
#include "input/RotaryEncoderInterruptImpl1.h"
|
||||||
#include "input/UpDownInterruptImpl1.h"
|
#include "input/UpDownInterruptImpl1.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "mesh/Default.h"
|
|
||||||
#include "mesh/MeshTypes.h"
|
#include "mesh/MeshTypes.h"
|
||||||
#include "modules/AdminModule.h"
|
#include "modules/AdminModule.h"
|
||||||
#include "modules/CannedMessageModule.h"
|
#include "modules/CannedMessageModule.h"
|
||||||
@ -121,7 +120,6 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
|||||||
auto changes = SEGMENT_CONFIG;
|
auto changes = SEGMENT_CONFIG;
|
||||||
|
|
||||||
// This is needed as we wait til picking the LoRa region to generate keys for the first time.
|
// This is needed as we wait til picking the LoRa region to generate keys for the first time.
|
||||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
|
|
||||||
if (!owner.is_licensed) {
|
if (!owner.is_licensed) {
|
||||||
bool keygenSuccess = false;
|
bool keygenSuccess = false;
|
||||||
if (config.security.private_key.size == 32) {
|
if (config.security.private_key.size == 32) {
|
||||||
@ -142,7 +140,6 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
|||||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
config.lora.tx_enabled = true;
|
config.lora.tx_enabled = true;
|
||||||
initRegion();
|
initRegion();
|
||||||
if (myRegion->dutyCycle < 100) {
|
if (myRegion->dutyCycle < 100) {
|
||||||
@ -578,7 +575,7 @@ void menuHandler::textMessageBaseMenu()
|
|||||||
|
|
||||||
void menuHandler::systemBaseMenu()
|
void menuHandler::systemBaseMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, WiFiToggle, PowerMenu, Test, enumEnd };
|
enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, PowerMenu, Test, enumEnd };
|
||||||
static const char *optionsArray[enumEnd] = {"Back"};
|
static const char *optionsArray[enumEnd] = {"Back"};
|
||||||
static int optionsEnumArray[enumEnd] = {Back};
|
static int optionsEnumArray[enumEnd] = {Back};
|
||||||
int options = 1;
|
int options = 1;
|
||||||
@ -594,10 +591,6 @@ void menuHandler::systemBaseMenu()
|
|||||||
optionsArray[options] = "Bluetooth Toggle";
|
optionsArray[options] = "Bluetooth Toggle";
|
||||||
#endif
|
#endif
|
||||||
optionsEnumArray[options++] = Bluetooth;
|
optionsEnumArray[options++] = Bluetooth;
|
||||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
|
||||||
optionsArray[options] = "WiFi Toggle";
|
|
||||||
optionsEnumArray[options++] = WiFiToggle;
|
|
||||||
#endif
|
|
||||||
#if defined(M5STACK_UNITC6L)
|
#if defined(M5STACK_UNITC6L)
|
||||||
optionsArray[options] = "Power";
|
optionsArray[options] = "Power";
|
||||||
#else
|
#else
|
||||||
@ -635,11 +628,6 @@ void menuHandler::systemBaseMenu()
|
|||||||
} else if (selected == Bluetooth) {
|
} else if (selected == Bluetooth) {
|
||||||
menuQueue = bluetooth_toggle_menu;
|
menuQueue = bluetooth_toggle_menu;
|
||||||
screen->runNow();
|
screen->runNow();
|
||||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
|
||||||
} else if (selected == WiFiToggle) {
|
|
||||||
menuQueue = wifi_toggle_menu;
|
|
||||||
screen->runNow();
|
|
||||||
#endif
|
|
||||||
} else if (selected == Back && !test_enabled) {
|
} else if (selected == Back && !test_enabled) {
|
||||||
test_count++;
|
test_count++;
|
||||||
if (test_count > 4) {
|
if (test_count > 4) {
|
||||||
@ -948,9 +936,7 @@ void menuHandler::BluetoothToggleMenu()
|
|||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 3;
|
bannerOptions.optionsCount = 3;
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected == 0)
|
if (selected == 1 || selected == 2) {
|
||||||
return;
|
|
||||||
else if (selected != (config.bluetooth.enabled ? 1 : 2)) {
|
|
||||||
InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0};
|
InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0};
|
||||||
inputBroker->injectInputEvent(&event);
|
inputBroker->injectInputEvent(&event);
|
||||||
}
|
}
|
||||||
@ -1049,8 +1035,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
|||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 10;
|
bannerOptions.optionsCount = 10;
|
||||||
bannerOptions.bannerCallback = [display](int selected) -> void {
|
bannerOptions.bannerCallback = [display](int selected) -> void {
|
||||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \
|
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || HAS_TFT
|
||||||
HAS_TFT || defined(HACKADAY_COMMUNICATOR)
|
|
||||||
uint8_t TFT_MESH_r = 0;
|
uint8_t TFT_MESH_r = 0;
|
||||||
uint8_t TFT_MESH_g = 0;
|
uint8_t TFT_MESH_g = 0;
|
||||||
uint8_t TFT_MESH_b = 0;
|
uint8_t TFT_MESH_b = 0;
|
||||||
@ -1290,28 +1275,19 @@ void menuHandler::wifiBaseMenu()
|
|||||||
|
|
||||||
void menuHandler::wifiToggleMenu()
|
void menuHandler::wifiToggleMenu()
|
||||||
{
|
{
|
||||||
enum optionsNumbers { Back, Wifi_disable, Wifi_enable };
|
enum optionsNumbers { Back, Wifi_toggle };
|
||||||
|
|
||||||
static const char *optionsArray[] = {"Back", "WiFi Disabled", "WiFi Enabled"};
|
static const char *optionsArray[] = {"Back", "Disable"};
|
||||||
BannerOverlayOptions bannerOptions;
|
BannerOverlayOptions bannerOptions;
|
||||||
bannerOptions.message = "WiFi Actions";
|
bannerOptions.message = "Disable Wifi and\nEnable Bluetooth?";
|
||||||
bannerOptions.optionsArrayPtr = optionsArray;
|
bannerOptions.optionsArrayPtr = optionsArray;
|
||||||
bannerOptions.optionsCount = 3;
|
bannerOptions.optionsCount = 2;
|
||||||
if (config.network.wifi_enabled == true)
|
|
||||||
bannerOptions.InitialSelected = 2;
|
|
||||||
else
|
|
||||||
bannerOptions.InitialSelected = 1;
|
|
||||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||||
if (selected == Wifi_disable) {
|
if (selected == Wifi_toggle) {
|
||||||
config.network.wifi_enabled = false;
|
config.network.wifi_enabled = false;
|
||||||
config.bluetooth.enabled = true;
|
config.bluetooth.enabled = true;
|
||||||
service->reloadConfig(SEGMENT_CONFIG);
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
||||||
} else if (selected == Wifi_enable) {
|
|
||||||
config.network.wifi_enabled = true;
|
|
||||||
config.bluetooth.enabled = false;
|
|
||||||
service->reloadConfig(SEGMENT_CONFIG);
|
|
||||||
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
screen->showOverlayBanner(bannerOptions);
|
screen->showOverlayBanner(bannerOptions);
|
||||||
@ -1359,7 +1335,7 @@ void menuHandler::screenOptionsMenu()
|
|||||||
static int optionsEnumArray[5] = {Back};
|
static int optionsEnumArray[5] = {Back};
|
||||||
int options = 1;
|
int options = 1;
|
||||||
|
|
||||||
#if defined(T_DECK) || defined(T_LORA_PAGER) || defined(HACKADAY_COMMUNICATOR)
|
#if defined(T_DECK) || defined(T_LORA_PAGER)
|
||||||
optionsArray[options] = "Show Long/Short Name";
|
optionsArray[options] = "Show Long/Short Name";
|
||||||
optionsEnumArray[options++] = NodeNameLength;
|
optionsEnumArray[options++] = NodeNameLength;
|
||||||
#endif
|
#endif
|
||||||
@ -1371,13 +1347,12 @@ void menuHandler::screenOptionsMenu()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only show screen color for TFT displays
|
// Only show screen color for TFT displays
|
||||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \
|
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || HAS_TFT
|
||||||
HAS_TFT || defined(HACKADAY_COMMUNICATOR)
|
|
||||||
optionsArray[options] = "Screen Color";
|
optionsArray[options] = "Screen Color";
|
||||||
optionsEnumArray[options++] = ScreenColor;
|
optionsEnumArray[options++] = ScreenColor;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
optionsArray[options] = "Frame Visibility Toggle";
|
optionsArray[options] = "Frame Visiblity Toggle";
|
||||||
optionsEnumArray[options++] = FrameToggles;
|
optionsEnumArray[options++] = FrameToggles;
|
||||||
|
|
||||||
optionsArray[options] = "Display Units";
|
optionsArray[options] = "Display Units";
|
||||||
|
|||||||
@ -85,13 +85,9 @@ void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiStat
|
|||||||
|
|
||||||
void NotificationRenderer::resetBanner()
|
void NotificationRenderer::resetBanner()
|
||||||
{
|
{
|
||||||
notificationTypeEnum previousType = current_notification_type;
|
|
||||||
|
|
||||||
alertBannerMessage[0] = '\0';
|
alertBannerMessage[0] = '\0';
|
||||||
current_notification_type = notificationTypeEnum::none;
|
current_notification_type = notificationTypeEnum::none;
|
||||||
|
|
||||||
OnScreenKeyboardModule::instance().clearPopup();
|
|
||||||
|
|
||||||
inEvent.inputEvent = INPUT_BROKER_NONE;
|
inEvent.inputEvent = INPUT_BROKER_NONE;
|
||||||
inEvent.kbchar = 0;
|
inEvent.kbchar = 0;
|
||||||
curSelected = 0;
|
curSelected = 0;
|
||||||
@ -104,13 +100,6 @@ void NotificationRenderer::resetBanner()
|
|||||||
currentNumber = 0;
|
currentNumber = 0;
|
||||||
|
|
||||||
nodeDB->pause_sort(false);
|
nodeDB->pause_sort(false);
|
||||||
|
|
||||||
// If we're exiting from text_input (virtual keyboard), stop module and trigger frame update
|
|
||||||
// to ensure any messages received during keyboard use are now displayed
|
|
||||||
if (previousType == notificationTypeEnum::text_input && screen) {
|
|
||||||
OnScreenKeyboardModule::instance().stop(false);
|
|
||||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state)
|
void NotificationRenderer::drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state)
|
||||||
@ -174,15 +163,13 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS
|
|||||||
// modulo to extract
|
// modulo to extract
|
||||||
uint8_t this_digit = (currentNumber % (pow_of_10(numDigits - curSelected))) / (pow_of_10(numDigits - curSelected - 1));
|
uint8_t this_digit = (currentNumber % (pow_of_10(numDigits - curSelected))) / (pow_of_10(numDigits - curSelected - 1));
|
||||||
// Handle input
|
// Handle input
|
||||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS ||
|
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||||
inEvent.inputEvent == INPUT_BROKER_UP_LONG) {
|
|
||||||
if (this_digit == 9) {
|
if (this_digit == 9) {
|
||||||
currentNumber -= 9 * (pow_of_10(numDigits - curSelected - 1));
|
currentNumber -= 9 * (pow_of_10(numDigits - curSelected - 1));
|
||||||
} else {
|
} else {
|
||||||
currentNumber += (pow_of_10(numDigits - curSelected - 1));
|
currentNumber += (pow_of_10(numDigits - curSelected - 1));
|
||||||
}
|
}
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS ||
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||||
inEvent.inputEvent == INPUT_BROKER_DOWN_LONG) {
|
|
||||||
if (this_digit == 0) {
|
if (this_digit == 0) {
|
||||||
currentNumber += 9 * (pow_of_10(numDigits - curSelected - 1));
|
currentNumber += 9 * (pow_of_10(numDigits - curSelected - 1));
|
||||||
} else {
|
} else {
|
||||||
@ -264,10 +251,10 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
|
|||||||
|
|
||||||
// Handle input
|
// Handle input
|
||||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
||||||
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS || inEvent.inputEvent == INPUT_BROKER_UP_LONG) {
|
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||||
curSelected--;
|
curSelected--;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
||||||
inEvent.inputEvent == INPUT_BROKER_USER_PRESS || inEvent.inputEvent == INPUT_BROKER_DOWN_LONG) {
|
inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||||
curSelected++;
|
curSelected++;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||||
alertBannerCallback(selectedNodenum);
|
alertBannerCallback(selectedNodenum);
|
||||||
@ -381,10 +368,10 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
|||||||
// Handle input
|
// Handle input
|
||||||
if (alertBannerOptions > 0) {
|
if (alertBannerOptions > 0) {
|
||||||
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
if (inEvent.inputEvent == INPUT_BROKER_UP || inEvent.inputEvent == INPUT_BROKER_LEFT ||
|
||||||
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS || inEvent.inputEvent == INPUT_BROKER_UP_LONG) {
|
inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||||
curSelected--;
|
curSelected--;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN || inEvent.inputEvent == INPUT_BROKER_RIGHT ||
|
||||||
inEvent.inputEvent == INPUT_BROKER_USER_PRESS || inEvent.inputEvent == INPUT_BROKER_DOWN_LONG) {
|
inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||||
curSelected++;
|
curSelected++;
|
||||||
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||||
if (optionsEnumPtr != nullptr) {
|
if (optionsEnumPtr != nullptr) {
|
||||||
@ -782,8 +769,40 @@ void NotificationRenderer::drawTextInput(OLEDDisplay *display, OLEDDisplayUiStat
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (inEvent.inputEvent != INPUT_BROKER_NONE) {
|
if (inEvent.inputEvent != INPUT_BROKER_NONE) {
|
||||||
bool handled = OnScreenKeyboardModule::processVirtualKeyboardInput(inEvent, virtualKeyboard);
|
if (inEvent.inputEvent == INPUT_BROKER_UP) {
|
||||||
if (!handled && inEvent.inputEvent == INPUT_BROKER_CANCEL) {
|
// high frequency for move cursor left/right than up/down with encoders
|
||||||
|
extern ::RotaryEncoderInterruptImpl1 *rotaryEncoderInterruptImpl1;
|
||||||
|
extern ::UpDownInterruptImpl1 *upDownInterruptImpl1;
|
||||||
|
if (::rotaryEncoderInterruptImpl1 || ::upDownInterruptImpl1) {
|
||||||
|
virtualKeyboard->moveCursorLeft();
|
||||||
|
} else {
|
||||||
|
virtualKeyboard->moveCursorUp();
|
||||||
|
}
|
||||||
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN) {
|
||||||
|
extern ::RotaryEncoderInterruptImpl1 *rotaryEncoderInterruptImpl1;
|
||||||
|
extern ::UpDownInterruptImpl1 *upDownInterruptImpl1;
|
||||||
|
if (::rotaryEncoderInterruptImpl1 || ::upDownInterruptImpl1) {
|
||||||
|
virtualKeyboard->moveCursorRight();
|
||||||
|
} else {
|
||||||
|
virtualKeyboard->moveCursorDown();
|
||||||
|
}
|
||||||
|
} else if (inEvent.inputEvent == INPUT_BROKER_LEFT) {
|
||||||
|
virtualKeyboard->moveCursorLeft();
|
||||||
|
} else if (inEvent.inputEvent == INPUT_BROKER_RIGHT) {
|
||||||
|
virtualKeyboard->moveCursorRight();
|
||||||
|
} else if (inEvent.inputEvent == INPUT_BROKER_UP_LONG) {
|
||||||
|
virtualKeyboard->moveCursorUp();
|
||||||
|
} else if (inEvent.inputEvent == INPUT_BROKER_DOWN_LONG) {
|
||||||
|
virtualKeyboard->moveCursorDown();
|
||||||
|
} else if (inEvent.inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||||
|
virtualKeyboard->moveCursorLeft();
|
||||||
|
} else if (inEvent.inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||||
|
virtualKeyboard->moveCursorRight();
|
||||||
|
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT) {
|
||||||
|
virtualKeyboard->handlePress();
|
||||||
|
} else if (inEvent.inputEvent == INPUT_BROKER_SELECT_LONG) {
|
||||||
|
virtualKeyboard->handleLongPress();
|
||||||
|
} else if (inEvent.inputEvent == INPUT_BROKER_CANCEL) {
|
||||||
auto callback = textInputCallback;
|
auto callback = textInputCallback;
|
||||||
delete virtualKeyboard;
|
delete virtualKeyboard;
|
||||||
virtualKeyboard = nullptr;
|
virtualKeyboard = nullptr;
|
||||||
@ -802,28 +821,12 @@ void NotificationRenderer::drawTextInput(OLEDDisplay *display, OLEDDisplayUiStat
|
|||||||
inEvent.inputEvent = INPUT_BROKER_NONE;
|
inEvent.inputEvent = INPUT_BROKER_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-check pointer before drawing to avoid use-after-free and crashes
|
|
||||||
if (!virtualKeyboard) {
|
|
||||||
// Ensure we exit text_input state and restore frames
|
|
||||||
if (current_notification_type == notificationTypeEnum::text_input) {
|
|
||||||
resetBanner();
|
|
||||||
}
|
|
||||||
if (screen) {
|
|
||||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
|
||||||
}
|
|
||||||
// If screen is null, do nothing (safe fallback)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the screen to avoid overlapping with underlying frames or overlays
|
// Clear the screen to avoid overlapping with underlying frames or overlays
|
||||||
display->setColor(BLACK);
|
display->setColor(BLACK);
|
||||||
display->fillRect(0, 0, display->getWidth(), display->getHeight());
|
display->fillRect(0, 0, display->getWidth(), display->getHeight());
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
// Draw the virtual keyboard
|
// Draw the virtual keyboard
|
||||||
virtualKeyboard->draw(display, 0, 0);
|
virtualKeyboard->draw(display, 0, 0);
|
||||||
|
|
||||||
// Draw transient popup overlay (if any) managed by OnScreenKeyboardModule
|
|
||||||
OnScreenKeyboardModule::instance().drawPopupOverlay(display);
|
|
||||||
} else {
|
} else {
|
||||||
// If virtualKeyboard is null, reset the banner to avoid getting stuck
|
// If virtualKeyboard is null, reset the banner to avoid getting stuck
|
||||||
LOG_INFO("Virtual keyboard is null - resetting banner");
|
LOG_INFO("Virtual keyboard is null - resetting banner");
|
||||||
@ -836,12 +839,5 @@ bool NotificationRenderer::isOverlayBannerShowing()
|
|||||||
return strlen(alertBannerMessage) > 0 && (alertBannerUntil == 0 || millis() <= alertBannerUntil);
|
return strlen(alertBannerMessage) > 0 && (alertBannerUntil == 0 || millis() <= alertBannerUntil);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NotificationRenderer::showKeyboardMessagePopupWithTitle(const char *title, const char *content, uint32_t durationMs)
|
|
||||||
{
|
|
||||||
if (!title || !content || current_notification_type != notificationTypeEnum::text_input)
|
|
||||||
return;
|
|
||||||
OnScreenKeyboardModule::instance().showPopup(title, content, durationMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
#endif
|
#endif
|
||||||
@ -4,7 +4,6 @@
|
|||||||
#include "OLEDDisplayUi.h"
|
#include "OLEDDisplayUi.h"
|
||||||
#include "graphics/Screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "graphics/VirtualKeyboard.h"
|
#include "graphics/VirtualKeyboard.h"
|
||||||
#include "modules/OnScreenKeyboardModule.h"
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#define MAX_LINES 5
|
#define MAX_LINES 5
|
||||||
@ -32,7 +31,6 @@ class NotificationRenderer
|
|||||||
static bool pauseBanner;
|
static bool pauseBanner;
|
||||||
|
|
||||||
static void resetBanner();
|
static void resetBanner();
|
||||||
static void showKeyboardMessagePopupWithTitle(const char *title, const char *content, uint32_t durationMs);
|
|
||||||
static void drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state);
|
static void drawBannercallback(OLEDDisplay *display, OLEDDisplayUiState *state);
|
||||||
static void drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state);
|
static void drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state);
|
||||||
static void drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiState *state);
|
static void drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiState *state);
|
||||||
|
|||||||
@ -11,7 +11,6 @@
|
|||||||
#include "graphics/Screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "graphics/ScreenFonts.h"
|
#include "graphics/ScreenFonts.h"
|
||||||
#include "graphics/SharedUIDisplay.h"
|
#include "graphics/SharedUIDisplay.h"
|
||||||
#include "graphics/TimeFormatters.h"
|
|
||||||
#include "graphics/images.h"
|
#include "graphics/images.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
@ -257,8 +256,7 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
|
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \
|
||||||
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
|
|
||||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||||
|
|
||||||
if (isHighResolution) {
|
if (isHighResolution) {
|
||||||
@ -385,7 +383,17 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
|||||||
// === 4. Uptime (only show if metric is present) ===
|
// === 4. Uptime (only show if metric is present) ===
|
||||||
char uptimeStr[32] = "";
|
char uptimeStr[32] = "";
|
||||||
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
|
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
|
||||||
getUptimeStr(node->device_metrics.uptime_seconds * 1000, " Up", uptimeStr, sizeof(uptimeStr));
|
uint32_t uptime = node->device_metrics.uptime_seconds;
|
||||||
|
uint32_t days = uptime / 86400;
|
||||||
|
uint32_t hours = (uptime % 86400) / 3600;
|
||||||
|
uint32_t mins = (uptime % 3600) / 60;
|
||||||
|
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
|
||||||
|
if (days)
|
||||||
|
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %ud %uh", days, hours);
|
||||||
|
else if (hours)
|
||||||
|
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %uh %um", hours, mins);
|
||||||
|
else
|
||||||
|
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
|
||||||
}
|
}
|
||||||
if (uptimeStr[0] && line < 5) {
|
if (uptimeStr[0] && line < 5) {
|
||||||
display->drawString(x, getTextPositions(display)[line++], uptimeStr);
|
display->drawString(x, getTextPositions(display)[line++], uptimeStr);
|
||||||
@ -584,8 +592,18 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
|||||||
drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
|
drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
|
||||||
#endif
|
#endif
|
||||||
char uptimeStr[32] = "";
|
char uptimeStr[32] = "";
|
||||||
|
uint32_t uptime = millis() / 1000;
|
||||||
|
uint32_t days = uptime / 86400;
|
||||||
|
uint32_t hours = (uptime % 86400) / 3600;
|
||||||
|
uint32_t mins = (uptime % 3600) / 60;
|
||||||
|
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
|
||||||
#if !defined(M5STACK_UNITC6L)
|
#if !defined(M5STACK_UNITC6L)
|
||||||
getUptimeStr(millis(), "Up", uptimeStr, sizeof(uptimeStr));
|
if (days)
|
||||||
|
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %ud %uh", days, hours);
|
||||||
|
else if (hours)
|
||||||
|
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %uh %um", hours, mins);
|
||||||
|
else
|
||||||
|
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %um", mins);
|
||||||
#endif
|
#endif
|
||||||
display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr);
|
display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr);
|
||||||
|
|
||||||
@ -1030,17 +1048,36 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
|||||||
if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) {
|
if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) {
|
||||||
// === Second Row: Last GPS Fix ===
|
// === Second Row: Last GPS Fix ===
|
||||||
if (gpsStatus->getLastFixMillis() > 0) {
|
if (gpsStatus->getLastFixMillis() > 0) {
|
||||||
uint32_t delta = millis() - gpsStatus->getLastFixMillis();
|
uint32_t delta = (millis() - gpsStatus->getLastFixMillis()) / 1000; // seconds since last fix
|
||||||
char uptimeStr[32];
|
uint32_t days = delta / 86400;
|
||||||
|
uint32_t hours = (delta % 86400) / 3600;
|
||||||
|
uint32_t mins = (delta % 3600) / 60;
|
||||||
|
uint32_t secs = delta % 60;
|
||||||
|
|
||||||
|
char buf[32];
|
||||||
#if defined(USE_EINK)
|
#if defined(USE_EINK)
|
||||||
// E-Ink: skip seconds, show only days/hours/mins
|
// E-Ink: skip seconds, show only days/hours/mins
|
||||||
getUptimeStr(delta, "Last", uptimeStr, sizeof(uptimeStr), false);
|
if (days > 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
|
||||||
|
} else if (hours > 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %um", mins);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
// Non E-Ink: include seconds where useful
|
// Non E-Ink: include seconds where useful
|
||||||
getUptimeStr(delta, "Last", uptimeStr, sizeof(uptimeStr), true);
|
if (days > 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
|
||||||
|
} else if (hours > 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
|
||||||
|
} else if (mins > 0) {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %um %us", mins, secs);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof(buf), "Last: %us", secs);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
display->drawString(0, getTextPositions(display)[line++], uptimeStr);
|
display->drawString(0, getTextPositions(display)[line++], buf);
|
||||||
} else {
|
} else {
|
||||||
display->drawString(0, getTextPositions(display)[line++], "Last: ?");
|
display->drawString(0, getTextPositions(display)[line++], "Last: ?");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,7 @@ const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03
|
|||||||
|
|
||||||
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
|
||||||
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || \
|
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || \
|
||||||
defined(USE_ST7796) || defined(HACKADAY_COMMUNICATOR) || ARCH_PORTDUINO) && \
|
ARCH_PORTDUINO) && \
|
||||||
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
!defined(DISPLAY_FORCE_SMALL_FONTS)
|
||||||
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
|
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
|
||||||
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
|
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
|
||||||
|
|||||||
@ -2,21 +2,19 @@
|
|||||||
#define icon_width 50
|
#define icon_width 50
|
||||||
#define icon_height 28
|
#define icon_height 28
|
||||||
static uint8_t icon_bits[] = {
|
static uint8_t icon_bits[] = {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x00, 0x03,
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0xf6,0xef,0xdf,0x71,0x00,0x00,0x1c,
|
||||||
0x00, 0x00, 0x00, 0x80, 0x07, 0xC0, 0x07, 0x00, 0x00, 0x00, 0xC0, 0x1F,
|
0xf7,0xef,0xdf,0x71,0x00,0x00,0xbc,0xf7,0xef,0xdf,0x71,0x00,0x00,0xfc,0x77,
|
||||||
0xC0, 0x0F, 0x00, 0x00, 0x00, 0xE0, 0x0F, 0xE0, 0x0F, 0x00, 0x00, 0x00,
|
0xe0,0xc0,0x71,0x00,0x00,0xfc,0x77,0xe0,0xc0,0x71,0x00,0x00,0xfc,0xf7,0xef,
|
||||||
0xE0, 0x0F, 0xF0, 0x1F, 0x00, 0x00, 0x00, 0xF0, 0x07, 0xF0, 0x3F, 0x00,
|
0xdf,0x7f,0x00,0x00,0x5c,0xf7,0xef,0xdf,0x7f,0x00,0x00,0x1c,0xf7,0xef,0xdf,
|
||||||
0x00, 0x00, 0xF8, 0x03, 0xF8, 0x7F, 0x00, 0x00, 0x00, 0xF8, 0x03, 0xFC,
|
0x7f,0x00,0x00,0x1c,0x77,0x00,0xdc,0x71,0x00,0x00,0x1c,0x77,0x00,0xdc,0x71,
|
||||||
0x7F, 0x00, 0x00, 0x00, 0xFC, 0x01, 0xFC, 0xFE, 0x00, 0x00, 0x00, 0xFE,
|
0x00,0x00,0x1c,0xf7,0xef,0xdf,0x71,0x00,0x00,0x1c,0xf7,0xef,0xdf,0x71,0x00,
|
||||||
0x00, 0xFE, 0xFC, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x7F, 0xFC, 0x01, 0x00,
|
0x00,0x1c,0xf7,0xef,0xdf,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
0x00, 0x7F, 0x00, 0x3F, 0xF8, 0x03, 0x00, 0x80, 0x3F, 0x80, 0x3F, 0xF0,
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xc1,0x7c,0x7e,0x00,0x00,
|
||||||
0x07, 0x00, 0x80, 0x3F, 0xC0, 0x1F, 0xF0, 0x07, 0x00, 0xC0, 0x1F, 0xC0,
|
0x00,0x80,0xe3,0xfe,0xfe,0x00,0x00,0x00,0x80,0xf7,0xc6,0xc6,0x00,0x00,0x00,
|
||||||
0x0F, 0xE0, 0x0F, 0x00, 0xE0, 0x0F, 0xE0, 0x0F, 0xC0, 0x1F, 0x00, 0xE0,
|
0x80,0xff,0xc6,0xc6,0x00,0x00,0x00,0x80,0xdd,0xc6,0xc6,0x00,0x00,0x00,0x80,
|
||||||
0x0F, 0xF0, 0x07, 0x80, 0x1F, 0x00, 0xF0, 0x07, 0xF8, 0x03, 0x80, 0x3F,
|
0xc9,0xc6,0xc6,0x00,0x00,0x00,0x80,0xc1,0xc6,0xc6,0x00,0x00,0x00,0x80,0xc1,
|
||||||
0x00, 0xF8, 0x03, 0xF8, 0x03, 0x00, 0x7F, 0x00, 0xFC, 0x03, 0xFC, 0x01,
|
0xfe,0xfe,0x00,0x00,0x00,0x80,0xc1,0x7c,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
0x00, 0x7E, 0x00, 0xFC, 0x01, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0xFE, 0x00,
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
0xFE, 0x00, 0x00, 0xFC, 0x01, 0x7E, 0x00, 0x7F, 0x00, 0x00, 0xF8, 0x01,
|
0x00
|
||||||
0x7E, 0x00, 0x3E, 0x00, 0x00, 0xF8, 0x01, 0x38, 0x00, 0x3C, 0x00, 0x00,
|
};
|
||||||
0x70, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, };
|
|
||||||
#endif
|
#endif
|
||||||
@ -2,29 +2,15 @@
|
|||||||
#define icon_width 50
|
#define icon_width 50
|
||||||
#define icon_height 20
|
#define icon_height 20
|
||||||
static uint8_t icon_bits[] = {
|
static uint8_t icon_bits[] = {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xec,0xdf,0xbf,0xe3,0x00,0x00,0x38,
|
||||||
0x80, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80,
|
0xee,0xdf,0xbf,0xe3,0x00,0x00,0x78,0xef,0xdf,0xbf,0xe3,0x00,0x00,0xf8,0xef,
|
||||||
0x07, 0xc0, 0x07, 0x00, 0x00, 0x00, 0xc0, 0x0f,
|
0xc0,0x81,0xe3,0x00,0x00,0xf8,0xef,0xc0,0x81,0xe3,0x00,0x00,0xf8,0xef,0xdf,
|
||||||
0xc0, 0x0f, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xe0,
|
0xbf,0xff,0x00,0x00,0xb8,0xee,0xdf,0xbf,0xff,0x00,0x00,0x38,0xee,0xdf,0xbf,
|
||||||
0x0f, 0x00, 0x00, 0x00, 0xe0, 0x07, 0xf0, 0x1f,
|
0xff,0x00,0x00,0x38,0xee,0x00,0xb8,0xe3,0x00,0x00,0x38,0xee,0x00,0xb8,0xe3,
|
||||||
0x00, 0x00, 0x00, 0xf0, 0x03, 0xf0, 0x3f, 0x00,
|
0x00,0x00,0x38,0xee,0xdf,0xbf,0xe3,0x00,0x00,0x38,0xee,0xdf,0xbf,0xe3,0x00,
|
||||||
0x00, 0x00, 0xf8, 0x03, 0xf8, 0x7f, 0x00, 0x00,
|
0x00,0x38,0xee,0xdf,0xbf,0xe3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||||
0x00, 0xf8, 0x01, 0xfc, 0x7e, 0x00, 0x00, 0x00,
|
0x00,0x00,0x00,0x00,0x91,0x39,0x00,0x00,0x00,0x00,0x00,0x5b,0x4a,0x00,0x00,
|
||||||
0xfc, 0x00, 0xfc, 0xfc, 0x00, 0x00, 0x00, 0xfe,
|
0x00,0x00,0x00,0x55,0x4a,0x00,0x00,0x00,0x00,0x00,0x91,0x39,0x00,0x00,0x00,
|
||||||
0x00, 0x7e, 0xf8, 0x00, 0x00, 0x00, 0x7e, 0x00,
|
0x00,0x00,0x00,0x00,0x00
|
||||||
0x3f, 0xf8, 0x01, 0x00, 0x00, 0x3f, 0x00, 0x1f,
|
};
|
||||||
0xf0, 0x01, 0x00, 0x00, 0x1f, 0x80, 0x1f, 0xe0,
|
|
||||||
0x03, 0x00, 0x80, 0x1f, 0xc0, 0x0f, 0xe0, 0x03,
|
|
||||||
0x00, 0x80, 0x0f, 0xc0, 0x07, 0xc0, 0x07, 0x00,
|
|
||||||
0xc0, 0x0f, 0xe0, 0x07, 0x80, 0x0f, 0x00, 0xe0,
|
|
||||||
0x07, 0xf0, 0x03, 0x80, 0x1f, 0x00, 0xe0, 0x03,
|
|
||||||
0xf8, 0x03, 0x00, 0x1f, 0x00, 0xf0, 0x03, 0xf8,
|
|
||||||
0x01, 0x00, 0x3f, 0x00, 0xf8, 0x01, 0xfc, 0x00,
|
|
||||||
0x00, 0x7e, 0x00, 0xfc, 0x00, 0xfe, 0x00, 0x00,
|
|
||||||
0x7e, 0x00, 0xfc, 0x00, 0x7e, 0x00, 0x00, 0xfc,
|
|
||||||
0x00, 0x7e, 0x00, 0x3f, 0x00, 0x00, 0xf8, 0x00,
|
|
||||||
0x7e, 0x00, 0x3e, 0x00, 0x00, 0xf8, 0x00, 0x38,
|
|
||||||
0x00, 0x1c, 0x00, 0x00, 0x70, 0x00, 0x10, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00 };
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -124,7 +124,7 @@ uint32_t InkHUD::AppletFont::toUtf32(std::string utf8)
|
|||||||
utf32 |= (utf8.at(3) & 0b00111111);
|
utf32 |= (utf8.at(3) & 0b00111111);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return 0;
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return utf32;
|
return utf32;
|
||||||
|
|||||||
@ -5,6 +5,7 @@ A pattern / collection of resources for creating custom UIs, to target small gro
|
|||||||
For an example, see the `heltec-vision-master-e290-inkhud` platformio env.
|
For an example, see the `heltec-vision-master-e290-inkhud` platformio env.
|
||||||
|
|
||||||
- platformio.ini
|
- platformio.ini
|
||||||
|
|
||||||
- suppress default Meshtastic components (Screen, ButtonThread, etc)
|
- suppress default Meshtastic components (Screen, ButtonThread, etc)
|
||||||
- define `MESHTASTIC_INCLUDE_NICHE_GRAPHICS`
|
- define `MESHTASTIC_INCLUDE_NICHE_GRAPHICS`
|
||||||
- (possibly) Edit `build_src_filter` to include our new nicheGraphics.h file
|
- (possibly) Edit `build_src_filter` to include our new nicheGraphics.h file
|
||||||
|
|||||||
@ -1,217 +0,0 @@
|
|||||||
#if defined(HACKADAY_COMMUNICATOR)
|
|
||||||
|
|
||||||
#include "HackadayCommunicatorKeyboard.h"
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
#define _TCA8418_COLS 10
|
|
||||||
#define _TCA8418_ROWS 8
|
|
||||||
#define _TCA8418_NUM_KEYS 80
|
|
||||||
|
|
||||||
#define _TCA8418_MULTI_TAP_THRESHOLD 1500
|
|
||||||
|
|
||||||
using Key = TCA8418KeyboardBase::TCA8418Key;
|
|
||||||
|
|
||||||
constexpr uint8_t modifierRightShiftKey = 30;
|
|
||||||
constexpr uint8_t modifierRightShift = 0b0001;
|
|
||||||
constexpr uint8_t modifierLeftShiftKey = 76; // keynum -1
|
|
||||||
constexpr uint8_t modifierLeftShift = 0b0001;
|
|
||||||
// constexpr uint8_t modifierSymKey = 42;
|
|
||||||
// constexpr uint8_t modifierSym = 0b0010;
|
|
||||||
|
|
||||||
// Num chars per key, Modulus for rotating through characters
|
|
||||||
static uint8_t HackadayCommunicatorTapMod[_TCA8418_NUM_KEYS] = {
|
|
||||||
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
|
||||||
0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 0, 0, 0, 2, 1, 2, 2, 0, 1, 1, 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned char HackadayCommunicatorTapMap[_TCA8418_NUM_KEYS][2] = {{},
|
|
||||||
{},
|
|
||||||
{'+'},
|
|
||||||
{'9'},
|
|
||||||
{'8'},
|
|
||||||
{'7'},
|
|
||||||
{'2'},
|
|
||||||
{'3'},
|
|
||||||
{'4'},
|
|
||||||
{'5'},
|
|
||||||
{Key::ESC},
|
|
||||||
{'q', 'Q'},
|
|
||||||
{'w', 'W'},
|
|
||||||
{'e', 'E'},
|
|
||||||
{'r', 'R'},
|
|
||||||
{'t', 'T'},
|
|
||||||
{'y', 'Y'},
|
|
||||||
{'u', 'U'},
|
|
||||||
{'i', 'I'},
|
|
||||||
{'o', 'O'},
|
|
||||||
{Key::TAB},
|
|
||||||
{'a', 'A'},
|
|
||||||
{'s', 'S'},
|
|
||||||
{'d', 'D'},
|
|
||||||
{'f', 'F'},
|
|
||||||
{'g', 'G'},
|
|
||||||
{'h', 'H'},
|
|
||||||
{'j', 'J'},
|
|
||||||
{'k', 'K'},
|
|
||||||
{'l', 'L'},
|
|
||||||
{},
|
|
||||||
{'z', 'Z'},
|
|
||||||
{'x', 'X'},
|
|
||||||
{'c', 'C'},
|
|
||||||
{'v', 'V'},
|
|
||||||
{'b', 'B'},
|
|
||||||
{'n', 'N'},
|
|
||||||
{'m', 'M'},
|
|
||||||
{',', '<'},
|
|
||||||
{'.', '>'},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{'\\'},
|
|
||||||
{' '},
|
|
||||||
{},
|
|
||||||
{Key::RIGHT},
|
|
||||||
{Key::DOWN},
|
|
||||||
{Key::LEFT},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{'-'},
|
|
||||||
{'6', '^'},
|
|
||||||
{'5', '%'},
|
|
||||||
{'4', '$'},
|
|
||||||
{'[', '{'},
|
|
||||||
{']', '}'},
|
|
||||||
{'p', 'P'},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{'*'},
|
|
||||||
{'3', '#'},
|
|
||||||
{'2', '@'},
|
|
||||||
{'1', '!'},
|
|
||||||
{Key::SELECT},
|
|
||||||
{'\'', '"'},
|
|
||||||
{';', ':'},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{},
|
|
||||||
{'/', '?'},
|
|
||||||
{'='},
|
|
||||||
{'.', '>'},
|
|
||||||
{'0', ')'},
|
|
||||||
{},
|
|
||||||
{Key::UP},
|
|
||||||
{Key::BSP},
|
|
||||||
{}};
|
|
||||||
|
|
||||||
HackadayCommunicatorKeyboard::HackadayCommunicatorKeyboard()
|
|
||||||
: TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(-1), next_key(-1),
|
|
||||||
last_tap(0L), char_idx(0), tap_interval(0)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void HackadayCommunicatorKeyboard::reset(void)
|
|
||||||
{
|
|
||||||
TCA8418KeyboardBase::reset();
|
|
||||||
enableInterrupts();
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle multi-key presses (shift and alt)
|
|
||||||
void HackadayCommunicatorKeyboard::trigger()
|
|
||||||
{
|
|
||||||
uint8_t count = keyCount();
|
|
||||||
if (count == 0)
|
|
||||||
return;
|
|
||||||
for (uint8_t i = 0; i < count; ++i) {
|
|
||||||
uint8_t k = readRegister(TCA8418_REG_KEY_EVENT_A + i);
|
|
||||||
uint8_t key = k & 0x7F;
|
|
||||||
if (k & 0x80) {
|
|
||||||
pressed(key);
|
|
||||||
} else {
|
|
||||||
released();
|
|
||||||
state = Idle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HackadayCommunicatorKeyboard::pressed(uint8_t key)
|
|
||||||
{
|
|
||||||
if (state == Init || state == Busy) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modifierFlag && (millis() - last_modifier_time > _TCA8418_MULTI_TAP_THRESHOLD)) {
|
|
||||||
modifierFlag = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t next_key = 0;
|
|
||||||
int row = (key - 1) / 10;
|
|
||||||
int col = (key - 1) % 10;
|
|
||||||
if (row >= _TCA8418_ROWS || col >= _TCA8418_COLS) {
|
|
||||||
return; // Invalid key
|
|
||||||
}
|
|
||||||
|
|
||||||
next_key = row * _TCA8418_COLS + col;
|
|
||||||
state = Held;
|
|
||||||
|
|
||||||
uint32_t now = millis();
|
|
||||||
tap_interval = now - last_tap;
|
|
||||||
|
|
||||||
updateModifierFlag(next_key);
|
|
||||||
if (isModifierKey(next_key)) {
|
|
||||||
last_modifier_time = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tap_interval < 0) {
|
|
||||||
last_tap = 0;
|
|
||||||
state = Busy;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next_key != last_key || tap_interval > _TCA8418_MULTI_TAP_THRESHOLD) {
|
|
||||||
char_idx = 0;
|
|
||||||
} else {
|
|
||||||
char_idx += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_key = next_key;
|
|
||||||
last_tap = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HackadayCommunicatorKeyboard::released()
|
|
||||||
{
|
|
||||||
if (state != Held) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last_key < 0 || last_key >= _TCA8418_NUM_KEYS) {
|
|
||||||
last_key = -1;
|
|
||||||
state = Idle;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t now = millis();
|
|
||||||
last_tap = now;
|
|
||||||
if (HackadayCommunicatorTapMod[last_key])
|
|
||||||
queueEvent(HackadayCommunicatorTapMap[last_key][modifierFlag % HackadayCommunicatorTapMod[last_key]]);
|
|
||||||
if (isModifierKey(last_key) == false)
|
|
||||||
modifierFlag = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HackadayCommunicatorKeyboard::updateModifierFlag(uint8_t key)
|
|
||||||
{
|
|
||||||
if (key == modifierRightShiftKey) {
|
|
||||||
modifierFlag ^= modifierRightShift;
|
|
||||||
} else if (key == modifierLeftShiftKey) {
|
|
||||||
modifierFlag ^= modifierLeftShift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HackadayCommunicatorKeyboard::isModifierKey(uint8_t key)
|
|
||||||
{
|
|
||||||
return (key == modifierRightShiftKey || key == modifierLeftShiftKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
#include "TCA8418KeyboardBase.h"
|
|
||||||
|
|
||||||
class HackadayCommunicatorKeyboard : public TCA8418KeyboardBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HackadayCommunicatorKeyboard();
|
|
||||||
void reset(void);
|
|
||||||
void trigger(void) override;
|
|
||||||
virtual ~HackadayCommunicatorKeyboard() {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void pressed(uint8_t key) override;
|
|
||||||
void released(void) override;
|
|
||||||
|
|
||||||
void updateModifierFlag(uint8_t key);
|
|
||||||
bool isModifierKey(uint8_t key);
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint8_t modifierFlag; // Flag to indicate if a modifier key is pressed
|
|
||||||
uint32_t last_modifier_time; // Timestamp of the last modifier key press
|
|
||||||
int8_t last_key;
|
|
||||||
int8_t next_key;
|
|
||||||
uint32_t last_tap;
|
|
||||||
uint8_t char_idx;
|
|
||||||
int32_t tap_interval;
|
|
||||||
};
|
|
||||||
@ -52,7 +52,7 @@ int InputBroker::handleInputEvent(const InputEvent *event)
|
|||||||
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
|
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
|
||||||
|
|
||||||
if (event && event->inputEvent != INPUT_BROKER_NONE && externalNotificationModule &&
|
if (event && event->inputEvent != INPUT_BROKER_NONE && externalNotificationModule &&
|
||||||
moduleConfig.external_notification.enabled && externalNotificationModule->nagging()) {
|
moduleConfig.external_notification.enabled) {
|
||||||
externalNotificationModule->stopNow();
|
externalNotificationModule->stopNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,12 +3,6 @@
|
|||||||
#include "Observer.h"
|
#include "Observer.h"
|
||||||
#include "freertosinc.h"
|
#include "freertosinc.h"
|
||||||
|
|
||||||
#ifdef InputBrokerDebug
|
|
||||||
#define LOG_INPUT(...) LOG_DEBUG(__VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define LOG_INPUT(...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum input_broker_event {
|
enum input_broker_event {
|
||||||
INPUT_BROKER_NONE = 0,
|
INPUT_BROKER_NONE = 0,
|
||||||
INPUT_BROKER_SELECT = 10,
|
INPUT_BROKER_SELECT = 10,
|
||||||
|
|||||||
@ -2,8 +2,6 @@
|
|||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include <Throttle.h>
|
#include <Throttle.h>
|
||||||
|
|
||||||
SerialKeyboard *globalSerialKeyboard = nullptr;
|
|
||||||
|
|
||||||
#ifdef INPUTBROKER_SERIAL_TYPE
|
#ifdef INPUTBROKER_SERIAL_TYPE
|
||||||
#define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file
|
#define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file
|
||||||
|
|
||||||
@ -27,8 +25,6 @@ unsigned char KeyMap[3][4][10] = {{{'.', 'a', 'd', 'g', 'j', 'm', 'p', 't', 'w',
|
|||||||
SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name)
|
SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name)
|
||||||
{
|
{
|
||||||
this->_originName = name;
|
this->_originName = name;
|
||||||
|
|
||||||
globalSerialKeyboard = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SerialKeyboard::erase()
|
void SerialKeyboard::erase()
|
||||||
@ -89,21 +85,9 @@ int32_t SerialKeyboard::runOnce()
|
|||||||
e.source = this->_originName;
|
e.source = this->_originName;
|
||||||
// SELECT OR SEND OR CANCEL EVENT
|
// SELECT OR SEND OR CANCEL EVENT
|
||||||
if (!(shiftRegister2 & (1 << 3))) {
|
if (!(shiftRegister2 & (1 << 3))) {
|
||||||
if (shift > 0) {
|
e.inputEvent = INPUT_BROKER_UP;
|
||||||
e.inputEvent = INPUT_BROKER_ANYKEY; // REQUIRED
|
|
||||||
e.kbchar = 0x09; // TAB
|
|
||||||
shift = 0; // reset shift after TAB
|
|
||||||
} else {
|
|
||||||
e.inputEvent = INPUT_BROKER_LEFT;
|
|
||||||
}
|
|
||||||
} else if (!(shiftRegister2 & (1 << 2))) {
|
} else if (!(shiftRegister2 & (1 << 2))) {
|
||||||
if (shift > 0) {
|
|
||||||
e.inputEvent = INPUT_BROKER_ANYKEY; // REQUIRED
|
|
||||||
e.kbchar = 0x09; // TAB
|
|
||||||
shift = 0; // reset shift after TAB
|
|
||||||
} else {
|
|
||||||
e.inputEvent = INPUT_BROKER_RIGHT;
|
e.inputEvent = INPUT_BROKER_RIGHT;
|
||||||
}
|
|
||||||
e.kbchar = 0;
|
e.kbchar = 0;
|
||||||
} else if (!(shiftRegister2 & (1 << 1))) {
|
} else if (!(shiftRegister2 & (1 << 1))) {
|
||||||
e.inputEvent = INPUT_BROKER_SELECT;
|
e.inputEvent = INPUT_BROKER_SELECT;
|
||||||
|
|||||||
@ -8,8 +8,6 @@ class SerialKeyboard : public Observable<const InputEvent *>, public concurrency
|
|||||||
public:
|
public:
|
||||||
explicit SerialKeyboard(const char *name);
|
explicit SerialKeyboard(const char *name);
|
||||||
|
|
||||||
uint8_t getShift() const { return shift; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual int32_t runOnce() override;
|
virtual int32_t runOnce() override;
|
||||||
void erase();
|
void erase();
|
||||||
@ -25,5 +23,3 @@ class SerialKeyboard : public Observable<const InputEvent *>, public concurrency
|
|||||||
int quickPress = 0;
|
int quickPress = 0;
|
||||||
unsigned long lastPressTime = 0;
|
unsigned long lastPressTime = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern SerialKeyboard *globalSerialKeyboard;
|
|
||||||
@ -88,50 +88,6 @@ int32_t TrackballInterruptBase::runOnce()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (directionDetected && directionStartTime > 0) {
|
|
||||||
uint32_t directionDuration = millis() - directionStartTime;
|
|
||||||
uint8_t directionPressedNow = 0;
|
|
||||||
directionInterval++;
|
|
||||||
|
|
||||||
if (!digitalRead(_pinUp)) {
|
|
||||||
directionPressedNow = TB_ACTION_UP;
|
|
||||||
} else if (!digitalRead(_pinDown)) {
|
|
||||||
directionPressedNow = TB_ACTION_DOWN;
|
|
||||||
} else if (!digitalRead(_pinLeft)) {
|
|
||||||
directionPressedNow = TB_ACTION_LEFT;
|
|
||||||
} else if (!digitalRead(_pinRight)) {
|
|
||||||
directionPressedNow = TB_ACTION_RIGHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t DIRECTION_REPEAT_THRESHOLD = 3;
|
|
||||||
|
|
||||||
if (directionPressedNow == TB_ACTION_NONE) {
|
|
||||||
// Reset state
|
|
||||||
directionDetected = false;
|
|
||||||
directionStartTime = 0;
|
|
||||||
directionInterval = 0;
|
|
||||||
this->action = TB_ACTION_NONE;
|
|
||||||
} else if (directionDuration >= LONG_PRESS_DURATION && directionInterval >= DIRECTION_REPEAT_THRESHOLD) {
|
|
||||||
// repeat event when long press these direction.
|
|
||||||
switch (directionPressedNow) {
|
|
||||||
case TB_ACTION_UP:
|
|
||||||
e.inputEvent = this->_eventUp;
|
|
||||||
break;
|
|
||||||
case TB_ACTION_DOWN:
|
|
||||||
e.inputEvent = this->_eventDown;
|
|
||||||
break;
|
|
||||||
case TB_ACTION_LEFT:
|
|
||||||
e.inputEvent = this->_eventLeft;
|
|
||||||
break;
|
|
||||||
case TB_ACTION_RIGHT:
|
|
||||||
e.inputEvent = this->_eventRight;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
directionInterval = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(T_DECK) // T-deck gets a super-simple debounce on trackball
|
#if defined(T_DECK) // T-deck gets a super-simple debounce on trackball
|
||||||
if (this->action == TB_ACTION_PRESSED && !pressDetected) {
|
if (this->action == TB_ACTION_PRESSED && !pressDetected) {
|
||||||
// Start long press detection
|
// Start long press detection
|
||||||
@ -157,22 +113,17 @@ int32_t TrackballInterruptBase::runOnce()
|
|||||||
pressDetected = true;
|
pressDetected = true;
|
||||||
pressStartTime = millis();
|
pressStartTime = millis();
|
||||||
// Don't send event yet, wait to see if it's a long press
|
// Don't send event yet, wait to see if it's a long press
|
||||||
} else if (this->action == TB_ACTION_UP && !digitalRead(_pinUp) && !directionDetected) {
|
} else if (this->action == TB_ACTION_UP && !digitalRead(_pinUp)) {
|
||||||
directionDetected = true;
|
// LOG_DEBUG("Trackball event UP");
|
||||||
directionStartTime = millis();
|
|
||||||
e.inputEvent = this->_eventUp;
|
e.inputEvent = this->_eventUp;
|
||||||
// send event first,will automatically trigger every 50ms * 3 after 500ms
|
} else if (this->action == TB_ACTION_DOWN && !digitalRead(_pinDown)) {
|
||||||
} else if (this->action == TB_ACTION_DOWN && !digitalRead(_pinDown) && !directionDetected) {
|
// LOG_DEBUG("Trackball event DOWN");
|
||||||
directionDetected = true;
|
|
||||||
directionStartTime = millis();
|
|
||||||
e.inputEvent = this->_eventDown;
|
e.inputEvent = this->_eventDown;
|
||||||
} else if (this->action == TB_ACTION_LEFT && !digitalRead(_pinLeft) && !directionDetected) {
|
} else if (this->action == TB_ACTION_LEFT && !digitalRead(_pinLeft)) {
|
||||||
directionDetected = true;
|
// LOG_DEBUG("Trackball event LEFT");
|
||||||
directionStartTime = millis();
|
|
||||||
e.inputEvent = this->_eventLeft;
|
e.inputEvent = this->_eventLeft;
|
||||||
} else if (this->action == TB_ACTION_RIGHT && !digitalRead(_pinRight) && !directionDetected) {
|
} else if (this->action == TB_ACTION_RIGHT && !digitalRead(_pinRight)) {
|
||||||
directionDetected = true;
|
// LOG_DEBUG("Trackball event RIGHT");
|
||||||
directionStartTime = millis();
|
|
||||||
e.inputEvent = this->_eventRight;
|
e.inputEvent = this->_eventRight;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -49,14 +49,10 @@ class TrackballInterruptBase : public Observable<const InputEvent *>, public con
|
|||||||
|
|
||||||
// Long press detection for press button
|
// Long press detection for press button
|
||||||
uint32_t pressStartTime = 0;
|
uint32_t pressStartTime = 0;
|
||||||
uint32_t directionStartTime = 0;
|
|
||||||
uint8_t directionInterval = 0;
|
|
||||||
bool pressDetected = false;
|
bool pressDetected = false;
|
||||||
bool directionDetected = false;
|
|
||||||
uint32_t lastLongPressEventTime = 0;
|
uint32_t lastLongPressEventTime = 0;
|
||||||
uint32_t lastDirectionPressEventTime = 0;
|
|
||||||
static const uint32_t LONG_PRESS_DURATION = 500; // ms
|
static const uint32_t LONG_PRESS_DURATION = 500; // ms
|
||||||
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = 300; // ms - interval between repeated long press events
|
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = 500; // ms - interval between repeated long press events
|
||||||
|
|
||||||
private:
|
private:
|
||||||
input_broker_event _eventDown = INPUT_BROKER_NONE;
|
input_broker_event _eventDown = INPUT_BROKER_NONE;
|
||||||
|
|||||||
@ -3,14 +3,6 @@
|
|||||||
#include "InputBroker.h"
|
#include "InputBroker.h"
|
||||||
#include "mesh/NodeDB.h"
|
#include "mesh/NodeDB.h"
|
||||||
|
|
||||||
#ifndef UPDOWN_LONG_PRESS_DURATION
|
|
||||||
#define UPDOWN_LONG_PRESS_DURATION 300
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef UPDOWN_LONG_PRESS_REPEAT_INTERVAL
|
|
||||||
#define UPDOWN_LONG_PRESS_REPEAT_INTERVAL 300
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class UpDownInterruptBase : public Observable<const InputEvent *>, public concurrency::OSThread
|
class UpDownInterruptBase : public Observable<const InputEvent *>, public concurrency::OSThread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -48,8 +40,8 @@ class UpDownInterruptBase : public Observable<const InputEvent *>, public concur
|
|||||||
uint32_t lastPressLongEventTime = 0;
|
uint32_t lastPressLongEventTime = 0;
|
||||||
uint32_t lastUpLongEventTime = 0;
|
uint32_t lastUpLongEventTime = 0;
|
||||||
uint32_t lastDownLongEventTime = 0;
|
uint32_t lastDownLongEventTime = 0;
|
||||||
static const uint32_t LONG_PRESS_DURATION = UPDOWN_LONG_PRESS_DURATION;
|
static const uint32_t LONG_PRESS_DURATION = 300;
|
||||||
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = UPDOWN_LONG_PRESS_REPEAT_INTERVAL;
|
static const uint32_t LONG_PRESS_REPEAT_INTERVAL = 300;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8_t _pinDown = 0;
|
uint8_t _pinDown = 0;
|
||||||
|
|||||||
@ -7,8 +7,6 @@
|
|||||||
#include "TDeckProKeyboard.h"
|
#include "TDeckProKeyboard.h"
|
||||||
#elif defined(T_LORA_PAGER)
|
#elif defined(T_LORA_PAGER)
|
||||||
#include "TLoraPagerKeyboard.h"
|
#include "TLoraPagerKeyboard.h"
|
||||||
#elif defined(HACKADAY_COMMUNICATOR)
|
|
||||||
#include "HackadayCommunicatorKeyboard.h"
|
|
||||||
#else
|
#else
|
||||||
#include "TCA8418Keyboard.h"
|
#include "TCA8418Keyboard.h"
|
||||||
#endif
|
#endif
|
||||||
@ -22,8 +20,6 @@ KbI2cBase::KbI2cBase(const char *name)
|
|||||||
TCAKeyboard(*(new TDeckProKeyboard()))
|
TCAKeyboard(*(new TDeckProKeyboard()))
|
||||||
#elif defined(T_LORA_PAGER)
|
#elif defined(T_LORA_PAGER)
|
||||||
TCAKeyboard(*(new TLoraPagerKeyboard()))
|
TCAKeyboard(*(new TLoraPagerKeyboard()))
|
||||||
#elif defined(HACKADAY_COMMUNICATOR)
|
|
||||||
TCAKeyboard(*(new HackadayCommunicatorKeyboard()))
|
|
||||||
#else
|
#else
|
||||||
TCAKeyboard(*(new TCA8418Keyboard()))
|
TCAKeyboard(*(new TCA8418Keyboard()))
|
||||||
#endif
|
#endif
|
||||||
@ -332,7 +328,7 @@ int32_t KbI2cBase::runOnce()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (e.inputEvent != INPUT_BROKER_NONE) {
|
if (e.inputEvent != INPUT_BROKER_NONE) {
|
||||||
// LOG_DEBUG("TCA8418 Notifying: %i Char: %c", e.inputEvent, e.kbchar);
|
LOG_DEBUG("TCA8418 Notifying: %i Char: %c", e.inputEvent, e.kbchar);
|
||||||
this->notifyObservers(&e);
|
this->notifyObservers(&e);
|
||||||
}
|
}
|
||||||
TCAKeyboard.trigger();
|
TCAKeyboard.trigger();
|
||||||
|
|||||||
13
src/main.cpp
13
src/main.cpp
@ -394,10 +394,7 @@ void setup()
|
|||||||
io.pinMode(EXPANDS_GPIO_EN, OUTPUT);
|
io.pinMode(EXPANDS_GPIO_EN, OUTPUT);
|
||||||
io.digitalWrite(EXPANDS_GPIO_EN, HIGH);
|
io.digitalWrite(EXPANDS_GPIO_EN, HIGH);
|
||||||
io.pinMode(EXPANDS_SD_PULLEN, INPUT);
|
io.pinMode(EXPANDS_SD_PULLEN, INPUT);
|
||||||
#elif defined(HACKADAY_COMMUNICATOR)
|
|
||||||
pinMode(KB_INT, INPUT);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
concurrency::hasBeenSetup = true;
|
concurrency::hasBeenSetup = true;
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
SPISettings spiSettings(portduino_config.spiSpeed, MSBFIRST, SPI_MODE0);
|
SPISettings spiSettings(portduino_config.spiSpeed, MSBFIRST, SPI_MODE0);
|
||||||
@ -480,10 +477,6 @@ void setup()
|
|||||||
#ifdef RESET_OLED
|
#ifdef RESET_OLED
|
||||||
pinMode(RESET_OLED, OUTPUT);
|
pinMode(RESET_OLED, OUTPUT);
|
||||||
digitalWrite(RESET_OLED, 1);
|
digitalWrite(RESET_OLED, 1);
|
||||||
delay(2);
|
|
||||||
digitalWrite(RESET_OLED, 0);
|
|
||||||
delay(10);
|
|
||||||
digitalWrite(RESET_OLED, 1);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SENSOR_POWER_CTRL_PIN
|
#ifdef SENSOR_POWER_CTRL_PIN
|
||||||
@ -881,7 +874,7 @@ void setup()
|
|||||||
|
|
||||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
||||||
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
|
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
|
||||||
defined(USE_SPISSD1306) || defined(USE_ST7796) || defined(HACKADAY_COMMUNICATOR)
|
defined(USE_SPISSD1306)
|
||||||
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
|
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
|
||||||
#elif defined(ARCH_PORTDUINO)
|
#elif defined(ARCH_PORTDUINO)
|
||||||
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || portduino_config.displayPanel) &&
|
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || portduino_config.displayPanel) &&
|
||||||
@ -966,7 +959,6 @@ void setup()
|
|||||||
i2cScanner.reset();
|
i2cScanner.reset();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(MESHTASTIC_EXCLUDE_PKI)
|
|
||||||
// warn the user about a low entropy key
|
// warn the user about a low entropy key
|
||||||
if (nodeDB->keyIsLowEntropy && !nodeDB->hasWarned) {
|
if (nodeDB->keyIsLowEntropy && !nodeDB->hasWarned) {
|
||||||
LOG_WARN(LOW_ENTROPY_WARNING);
|
LOG_WARN(LOW_ENTROPY_WARNING);
|
||||||
@ -977,7 +969,6 @@ void setup()
|
|||||||
service->sendClientNotification(cn);
|
service->sendClientNotification(cn);
|
||||||
nodeDB->hasWarned = true;
|
nodeDB->hasWarned = true;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// buttons are now inputBroker, so have to come after setupModules
|
// buttons are now inputBroker, so have to come after setupModules
|
||||||
#if HAS_BUTTON
|
#if HAS_BUTTON
|
||||||
@ -1158,7 +1149,7 @@ void setup()
|
|||||||
// the current region name)
|
// the current region name)
|
||||||
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
|
||||||
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
|
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
|
||||||
defined(USE_ST7796) || defined(USE_SPISSD1306) || defined(HACKADAY_COMMUNICATOR)
|
defined(USE_SPISSD1306)
|
||||||
if (screen)
|
if (screen)
|
||||||
screen->setup();
|
screen->setup();
|
||||||
#elif defined(ARCH_PORTDUINO)
|
#elif defined(ARCH_PORTDUINO)
|
||||||
|
|||||||
@ -57,7 +57,14 @@ class Default
|
|||||||
// Note: Kept as uint32_t to match the public API parameter type
|
// Note: Kept as uint32_t to match the public API parameter type
|
||||||
static float congestionScalingCoefficient(uint32_t numOnlineNodes)
|
static float congestionScalingCoefficient(uint32_t numOnlineNodes)
|
||||||
{
|
{
|
||||||
if (numOnlineNodes <= 40) {
|
// Increase frequency of broadcasts for small networks regardless of preset
|
||||||
|
if (numOnlineNodes <= 10) {
|
||||||
|
return 0.6;
|
||||||
|
} else if (numOnlineNodes <= 20) {
|
||||||
|
return 0.7;
|
||||||
|
} else if (numOnlineNodes <= 30) {
|
||||||
|
return 0.8;
|
||||||
|
} else if (numOnlineNodes <= 40) {
|
||||||
return 1.0;
|
return 1.0;
|
||||||
} else {
|
} else {
|
||||||
float throttlingFactor = 0.075;
|
float throttlingFactor = 0.075;
|
||||||
|
|||||||
@ -244,8 +244,6 @@ template <typename T> void LR11x0Interface<T>::startReceive()
|
|||||||
// We use a 16 bit preamble so this should save some power by letting radio sit in standby mostly.
|
// We use a 16 bit preamble so this should save some power by letting radio sit in standby mostly.
|
||||||
int err =
|
int err =
|
||||||
lora.startReceive(RADIOLIB_LR11X0_RX_TIMEOUT_INF, MESHTASTIC_RADIOLIB_IRQ_RX_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0);
|
lora.startReceive(RADIOLIB_LR11X0_RX_TIMEOUT_INF, MESHTASTIC_RADIOLIB_IRQ_RX_FLAGS, RADIOLIB_IRQ_RX_DEFAULT_MASK, 0);
|
||||||
if (err)
|
|
||||||
LOG_ERROR("StartReceive error: %d", err);
|
|
||||||
assert(err == RADIOLIB_ERR_NONE);
|
assert(err == RADIOLIB_ERR_NONE);
|
||||||
|
|
||||||
RadioLibInterface::startReceive();
|
RadioLibInterface::startReceive();
|
||||||
|
|||||||
@ -664,8 +664,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
|||||||
config.bluetooth.fixed_pin = defaultBLEPin;
|
config.bluetooth.fixed_pin = defaultBLEPin;
|
||||||
|
|
||||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
|
||||||
defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(USE_SPISSD1306) || \
|
defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(USE_SPISSD1306)
|
||||||
defined(USE_ST7796) || defined(HACKADAY_COMMUNICATOR)
|
|
||||||
bool hasScreen = true;
|
bool hasScreen = true;
|
||||||
#ifdef HELTEC_MESH_NODE_T114
|
#ifdef HELTEC_MESH_NODE_T114
|
||||||
uint32_t st7789_id = get_st7789_id(ST7789_NSS, ST7789_SCK, ST7789_SDA, ST7789_RS, ST7789_RESET);
|
uint32_t st7789_id = get_st7789_id(ST7789_NSS, ST7789_SCK, ST7789_SDA, ST7789_RS, ST7789_RESET);
|
||||||
@ -735,9 +734,6 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
|||||||
config.display.screen_on_secs = 30;
|
config.display.screen_on_secs = 30;
|
||||||
config.display.wake_on_tap_or_motion = true;
|
config.display.wake_on_tap_or_motion = true;
|
||||||
#endif
|
#endif
|
||||||
#ifdef COMPASS_ORIENTATION
|
|
||||||
config.display.compass_orientation = COMPASS_ORIENTATION;
|
|
||||||
#endif
|
|
||||||
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI
|
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI
|
||||||
if (WiFiOTA::isUpdated()) {
|
if (WiFiOTA::isUpdated()) {
|
||||||
WiFiOTA::recoverConfig(&config.network);
|
WiFiOTA::recoverConfig(&config.network);
|
||||||
@ -2012,7 +2008,6 @@ UserLicenseStatus NodeDB::getLicenseStatus(uint32_t nodeNum)
|
|||||||
return info->user.is_licensed ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed;
|
return info->user.is_licensed ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(MESHTASTIC_EXCLUDE_PKI)
|
|
||||||
bool NodeDB::checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest)
|
bool NodeDB::checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest)
|
||||||
{
|
{
|
||||||
if (keyToTest.size == 32) {
|
if (keyToTest.size == 32) {
|
||||||
@ -2027,7 +2022,6 @@ bool NodeDB::checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_pub
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
|
bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -283,9 +283,7 @@ class NodeDB
|
|||||||
|
|
||||||
bool hasValidPosition(const meshtastic_NodeInfoLite *n);
|
bool hasValidPosition(const meshtastic_NodeInfoLite *n);
|
||||||
|
|
||||||
#if !defined(MESHTASTIC_EXCLUDE_PKI)
|
|
||||||
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
||||||
#endif
|
|
||||||
|
|
||||||
bool backupPreferences(meshtastic_AdminMessage_BackupLocation location);
|
bool backupPreferences(meshtastic_AdminMessage_BackupLocation location);
|
||||||
bool restorePreferences(meshtastic_AdminMessage_BackupLocation location,
|
bool restorePreferences(meshtastic_AdminMessage_BackupLocation location,
|
||||||
|
|||||||
@ -193,14 +193,15 @@ const RegionInfo regions[] = {
|
|||||||
RDEF(LORA_24, 2400.0f, 2483.5f, 100, 0, 10, true, false, true),
|
RDEF(LORA_24, 2400.0f, 2483.5f, 100, 0, 10, true, false, true),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Custom region
|
Without limits
|
||||||
*/
|
*/
|
||||||
RDEF(NO_LIMITS, 863.0f, 870.0f, 100, 0, 30, true, false, false),
|
RDEF(NO_LIMITS, 863.0f, 870.0f, 100, 0, 36, true, false, false),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This needs to be last. Same as US.
|
This needs to be last. Same as US.
|
||||||
*/
|
*/
|
||||||
RDEF(UNSET, 902.0f, 928.0f, 100, 0, 30, true, false, false)
|
RDEF(UNSET, 902.0f, 928.0f, 100, 0, 30, true, false, false)
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const RegionInfo *myRegion;
|
const RegionInfo *myRegion;
|
||||||
|
|||||||
@ -286,7 +286,7 @@ typedef enum _meshtastic_Config_LoRaConfig_RegionCode {
|
|||||||
meshtastic_Config_LoRaConfig_RegionCode_NP_865 = 25,
|
meshtastic_Config_LoRaConfig_RegionCode_NP_865 = 25,
|
||||||
/* Brazil 902MHz */
|
/* Brazil 902MHz */
|
||||||
meshtastic_Config_LoRaConfig_RegionCode_BR_902 = 26,
|
meshtastic_Config_LoRaConfig_RegionCode_BR_902 = 26,
|
||||||
/* NO_LIMITS */
|
/* NO LIMITS */
|
||||||
meshtastic_Config_LoRaConfig_RegionCode_NO_LIMITS = 27
|
meshtastic_Config_LoRaConfig_RegionCode_NO_LIMITS = 27
|
||||||
} meshtastic_Config_LoRaConfig_RegionCode;
|
} meshtastic_Config_LoRaConfig_RegionCode;
|
||||||
|
|
||||||
|
|||||||
@ -288,12 +288,6 @@ typedef enum _meshtastic_HardwareModel {
|
|||||||
meshtastic_HardwareModel_WISMESH_TAP_V2 = 116,
|
meshtastic_HardwareModel_WISMESH_TAP_V2 = 116,
|
||||||
/* RAK3401 */
|
/* RAK3401 */
|
||||||
meshtastic_HardwareModel_RAK3401 = 117,
|
meshtastic_HardwareModel_RAK3401 = 117,
|
||||||
/* RAK6421 Hat+ */
|
|
||||||
meshtastic_HardwareModel_RAK6421 = 118,
|
|
||||||
/* Elecrow ThinkNode M4 */
|
|
||||||
meshtastic_HardwareModel_THINKNODE_M4 = 119,
|
|
||||||
/* Elecrow ThinkNode M6 */
|
|
||||||
meshtastic_HardwareModel_THINKNODE_M6 = 120,
|
|
||||||
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||||
------------------------------------------------------------------------------------------------------------------------------------------ */
|
------------------------------------------------------------------------------------------------------------------------------------------ */
|
||||||
@ -831,11 +825,7 @@ typedef struct _meshtastic_MeshPacket {
|
|||||||
Note: Our crypto implementation uses this field as well.
|
Note: Our crypto implementation uses this field as well.
|
||||||
See [crypto](/docs/overview/encryption) for details. */
|
See [crypto](/docs/overview/encryption) for details. */
|
||||||
uint32_t from;
|
uint32_t from;
|
||||||
/* The (immediate) destination for this packet
|
/* The (immediate) destination for this packet */
|
||||||
If the value is 4,294,967,295 (maximum value of an unsigned 32bit integer), this indicates that the packet was
|
|
||||||
not destined for a specific node, but for a channel as indicated by the value of `channel` below.
|
|
||||||
If the value is another, this indicates that the packet was destined for a specific
|
|
||||||
node (i.e. a kind of "Direct Message" to this node) and not broadcast on a channel. */
|
|
||||||
uint32_t to;
|
uint32_t to;
|
||||||
/* (Usually) If set, this indicates the index in the secondary_channels table that this packet was sent/received on.
|
/* (Usually) If set, this indicates the index in the secondary_channels table that this packet was sent/received on.
|
||||||
If unset, packet was on the primary channel.
|
If unset, packet was on the primary channel.
|
||||||
|
|||||||
@ -773,7 +773,6 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
|||||||
config.lora = validatedLora;
|
config.lora = validatedLora;
|
||||||
// If we're setting region for the first time, init the region and regenerate the keys
|
// If we're setting region for the first time, init the region and regenerate the keys
|
||||||
if (isRegionUnset && config.lora.region > meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
if (isRegionUnset && config.lora.region > meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||||
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
|
|
||||||
if (!owner.is_licensed) {
|
if (!owner.is_licensed) {
|
||||||
bool keygenSuccess = false;
|
bool keygenSuccess = false;
|
||||||
if (config.security.private_key.size == 32) {
|
if (config.security.private_key.size == 32) {
|
||||||
@ -792,7 +791,6 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
|||||||
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
config.lora.tx_enabled = true;
|
config.lora.tx_enabled = true;
|
||||||
initRegion();
|
initRegion();
|
||||||
if (myRegion->dutyCycle < 100) {
|
if (myRegion->dutyCycle < 100) {
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
#include "graphics/draw/NotificationRenderer.h"
|
#include "graphics/draw/NotificationRenderer.h"
|
||||||
#include "graphics/emotes.h"
|
#include "graphics/emotes.h"
|
||||||
#include "graphics/images.h"
|
#include "graphics/images.h"
|
||||||
#include "input/SerialKeyboard.h"
|
|
||||||
#include "main.h" // for cardkb_found
|
#include "main.h" // for cardkb_found
|
||||||
#include "mesh/generated/meshtastic/cannedmessages.pb.h"
|
#include "mesh/generated/meshtastic/cannedmessages.pb.h"
|
||||||
#include "modules/AdminModule.h"
|
#include "modules/AdminModule.h"
|
||||||
@ -837,7 +836,6 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
|
|||||||
if (event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() > 0) {
|
if (event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() > 0) {
|
||||||
payload = 0x08;
|
payload = 0x08;
|
||||||
lastTouchMillis = millis();
|
lastTouchMillis = millis();
|
||||||
requestFocus();
|
|
||||||
runOnce();
|
runOnce();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -846,7 +844,6 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
|
|||||||
if (event->inputEvent == INPUT_BROKER_LEFT) {
|
if (event->inputEvent == INPUT_BROKER_LEFT) {
|
||||||
payload = INPUT_BROKER_LEFT;
|
payload = INPUT_BROKER_LEFT;
|
||||||
lastTouchMillis = millis();
|
lastTouchMillis = millis();
|
||||||
requestFocus();
|
|
||||||
runOnce();
|
runOnce();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -854,7 +851,6 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
|
|||||||
if (event->inputEvent == INPUT_BROKER_RIGHT) {
|
if (event->inputEvent == INPUT_BROKER_RIGHT) {
|
||||||
payload = INPUT_BROKER_RIGHT;
|
payload = INPUT_BROKER_RIGHT;
|
||||||
lastTouchMillis = millis();
|
lastTouchMillis = millis();
|
||||||
requestFocus();
|
|
||||||
runOnce();
|
runOnce();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1018,7 +1014,8 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
// Clean up virtual keyboard if needed when going inactive
|
// Clean up virtual keyboard if needed when going inactive
|
||||||
if (graphics::NotificationRenderer::virtualKeyboard && graphics::NotificationRenderer::textInputCallback == nullptr) {
|
if (graphics::NotificationRenderer::virtualKeyboard && graphics::NotificationRenderer::textInputCallback == nullptr) {
|
||||||
LOG_INFO("Performing delayed virtual keyboard cleanup");
|
LOG_INFO("Performing delayed virtual keyboard cleanup");
|
||||||
graphics::OnScreenKeyboardModule::instance().stop(false);
|
delete graphics::NotificationRenderer::virtualKeyboard;
|
||||||
|
graphics::NotificationRenderer::virtualKeyboard = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
temporaryMessage = "";
|
temporaryMessage = "";
|
||||||
@ -1035,7 +1032,9 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
// Clean up virtual keyboard after sending
|
// Clean up virtual keyboard after sending
|
||||||
if (graphics::NotificationRenderer::virtualKeyboard) {
|
if (graphics::NotificationRenderer::virtualKeyboard) {
|
||||||
LOG_INFO("Cleaning up virtual keyboard after message send");
|
LOG_INFO("Cleaning up virtual keyboard after message send");
|
||||||
graphics::OnScreenKeyboardModule::instance().stop(false);
|
delete graphics::NotificationRenderer::virtualKeyboard;
|
||||||
|
graphics::NotificationRenderer::virtualKeyboard = nullptr;
|
||||||
|
graphics::NotificationRenderer::textInputCallback = nullptr;
|
||||||
graphics::NotificationRenderer::resetBanner();
|
graphics::NotificationRenderer::resetBanner();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1093,7 +1092,9 @@ int32_t CannedMessageModule::runOnce()
|
|||||||
// Clean up virtual keyboard if it exists during timeout
|
// Clean up virtual keyboard if it exists during timeout
|
||||||
if (graphics::NotificationRenderer::virtualKeyboard) {
|
if (graphics::NotificationRenderer::virtualKeyboard) {
|
||||||
LOG_INFO("Cleaning up virtual keyboard due to module timeout");
|
LOG_INFO("Cleaning up virtual keyboard due to module timeout");
|
||||||
graphics::OnScreenKeyboardModule::instance().stop(false);
|
delete graphics::NotificationRenderer::virtualKeyboard;
|
||||||
|
graphics::NotificationRenderer::virtualKeyboard = nullptr;
|
||||||
|
graphics::NotificationRenderer::textInputCallback = nullptr;
|
||||||
graphics::NotificationRenderer::resetBanner();
|
graphics::NotificationRenderer::resetBanner();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1844,88 +1845,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
|||||||
display->drawString(x + display->getWidth() - display->getStringWidth(buffer), y + 0, buffer);
|
display->drawString(x + display->getWidth() - display->getStringWidth(buffer), y + 0, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if INPUTBROKER_SERIAL_TYPE == 1
|
// --- Draw Free Text input with multi-emote support and proper line wrapping ---
|
||||||
// Chatter Modifier key mode label (right side)
|
|
||||||
{
|
|
||||||
uint8_t mode = globalSerialKeyboard ? globalSerialKeyboard->getShift() : 0;
|
|
||||||
const char *label = (mode == 0) ? "a" : (mode == 1) ? "A" : "#";
|
|
||||||
|
|
||||||
display->setFont(FONT_SMALL);
|
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
||||||
|
|
||||||
const int16_t th = FONT_HEIGHT_SMALL;
|
|
||||||
const int16_t tw = display->getStringWidth(label);
|
|
||||||
const int16_t padX = 3;
|
|
||||||
const int16_t padY = 2;
|
|
||||||
const int16_t r = 3;
|
|
||||||
|
|
||||||
const int16_t bw = tw + padX * 2;
|
|
||||||
const int16_t bh = th + padY * 2;
|
|
||||||
|
|
||||||
const int16_t bx = x + display->getWidth() - bw - 2;
|
|
||||||
const int16_t by = y + display->getHeight() - bh - 2;
|
|
||||||
|
|
||||||
display->setColor(WHITE);
|
|
||||||
display->fillRect(bx + r, by, bw - r * 2, bh);
|
|
||||||
display->fillRect(bx, by + r, r, bh - r * 2);
|
|
||||||
display->fillRect(bx + bw - r, by + r, r, bh - r * 2);
|
|
||||||
display->fillCircle(bx + r, by + r, r);
|
|
||||||
display->fillCircle(bx + bw - r - 1, by + r, r);
|
|
||||||
display->fillCircle(bx + r, by + bh - r - 1, r);
|
|
||||||
display->fillCircle(bx + bw - r - 1, by + bh - r - 1, r);
|
|
||||||
|
|
||||||
display->setColor(BLACK);
|
|
||||||
display->drawString(bx + padX, by + padY, label);
|
|
||||||
}
|
|
||||||
|
|
||||||
// LEFT-SIDE DESTINATION-HINT BOX (“Dest: Shift + ◄”)
|
|
||||||
{
|
|
||||||
display->setFont(FONT_SMALL);
|
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
||||||
|
|
||||||
const char *label = "Dest: Shift + ";
|
|
||||||
int16_t labelW = display->getStringWidth(label);
|
|
||||||
|
|
||||||
// triangle size visually matches glyph height, not full line height
|
|
||||||
const int triH = FONT_HEIGHT_SMALL - 3;
|
|
||||||
const int triW = triH * 0.7;
|
|
||||||
|
|
||||||
const int16_t padX = 3;
|
|
||||||
const int16_t padY = 2;
|
|
||||||
const int16_t r = 3;
|
|
||||||
|
|
||||||
const int16_t bw = labelW + triW + padX * 2 + 2;
|
|
||||||
const int16_t bh = FONT_HEIGHT_SMALL + padY * 2;
|
|
||||||
|
|
||||||
const int16_t bx = x + 2;
|
|
||||||
const int16_t by = y + display->getHeight() - bh - 2;
|
|
||||||
|
|
||||||
// Rounded white box
|
|
||||||
display->setColor(WHITE);
|
|
||||||
display->fillRect(bx + r, by, bw - (r * 2), bh);
|
|
||||||
display->fillRect(bx, by + r, r, bh - (r * 2));
|
|
||||||
display->fillRect(bx + bw - r, by + r, r, bh - (r * 2));
|
|
||||||
display->fillCircle(bx + r, by + r, r);
|
|
||||||
display->fillCircle(bx + bw - r - 1, by + r, r);
|
|
||||||
display->fillCircle(bx + r, by + bh - r - 1, r);
|
|
||||||
display->fillCircle(bx + bw - r - 1, by + bh - r - 1, r);
|
|
||||||
|
|
||||||
// Draw text
|
|
||||||
display->setColor(BLACK);
|
|
||||||
display->drawString(bx + padX, by + padY, label);
|
|
||||||
|
|
||||||
// Perfectly center triangle on text baseline
|
|
||||||
int16_t tx = bx + padX + labelW;
|
|
||||||
int16_t ty = by + padY + (FONT_HEIGHT_SMALL / 2) - (triH / 2) - 1; // -1 for optical centering
|
|
||||||
|
|
||||||
// ◄ Left-pointing triangle
|
|
||||||
display->fillTriangle(tx + triW, ty, // top-right
|
|
||||||
tx, ty + triH / 2, // left center
|
|
||||||
tx + triW, ty + triH // bottom-right
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// Draw Free Text input with multi-emote support and proper line wrapping
|
|
||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
{
|
{
|
||||||
int inputY = 0 + y + FONT_HEIGHT_SMALL;
|
int inputY = 0 + y + FONT_HEIGHT_SMALL;
|
||||||
|
|||||||
@ -168,7 +168,7 @@ int32_t ExternalNotificationModule::runOnce()
|
|||||||
delay = EXT_NOTIFICATION_FAST_THREAD_MS;
|
delay = EXT_NOTIFICATION_FAST_THREAD_MS;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
#ifdef T_WATCH_S3
|
||||||
drv.go();
|
drv.go();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -283,7 +283,7 @@ void ExternalNotificationModule::setExternalState(uint8_t index, bool on)
|
|||||||
#ifdef UNPHONE
|
#ifdef UNPHONE
|
||||||
unphone.rgb(red, green, blue);
|
unphone.rgb(red, green, blue);
|
||||||
#endif
|
#endif
|
||||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
#ifdef T_WATCH_S3
|
||||||
if (on) {
|
if (on) {
|
||||||
drv.go();
|
drv.go();
|
||||||
} else {
|
} else {
|
||||||
@ -310,16 +310,18 @@ void ExternalNotificationModule::stopNow()
|
|||||||
rtttl::stop();
|
rtttl::stop();
|
||||||
#ifdef HAS_I2S
|
#ifdef HAS_I2S
|
||||||
LOG_INFO("Stop audioThread playback");
|
LOG_INFO("Stop audioThread playback");
|
||||||
|
if (audioThread->isPlaying())
|
||||||
audioThread->stop();
|
audioThread->stop();
|
||||||
#endif
|
#endif
|
||||||
// Turn off all outputs
|
// Turn off all outputs
|
||||||
LOG_INFO("Turning off setExternalStates");
|
LOG_INFO("Turning off setExternalStates: ");
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
setExternalState(i, false);
|
setExternalState(i, false);
|
||||||
externalTurnedOn[i] = 0;
|
externalTurnedOn[i] = 0;
|
||||||
|
LOG_INFO("%d ", i);
|
||||||
}
|
}
|
||||||
setIntervalFromNow(0);
|
setIntervalFromNow(0);
|
||||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
#ifdef T_WATCH_S3
|
||||||
drv.stop();
|
drv.stop();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -541,19 +543,6 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
|||||||
(!isBroadcast(mp.to) && isToUs(&mp))) {
|
(!isBroadcast(mp.to) && isToUs(&mp))) {
|
||||||
// Buzz if buzzer mode is not in DIRECT_MSG_ONLY or is DM to us
|
// Buzz if buzzer mode is not in DIRECT_MSG_ONLY or is DM to us
|
||||||
isNagging = true;
|
isNagging = true;
|
||||||
#ifdef T_LORA_PAGER
|
|
||||||
if (canBuzz()) {
|
|
||||||
drv.setWaveform(0, 16); // Long buzzer 100%
|
|
||||||
drv.setWaveform(1, 0); // Pause
|
|
||||||
drv.setWaveform(2, 16);
|
|
||||||
drv.setWaveform(3, 0);
|
|
||||||
drv.setWaveform(4, 16);
|
|
||||||
drv.setWaveform(5, 0);
|
|
||||||
drv.setWaveform(6, 16);
|
|
||||||
drv.setWaveform(7, 0);
|
|
||||||
drv.go();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) {
|
if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) {
|
||||||
setExternalState(2, true);
|
setExternalState(2, true);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -13,8 +13,6 @@
|
|||||||
#include "input/TrackballInterruptImpl1.h"
|
#include "input/TrackballInterruptImpl1.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "modules/StatusLEDModule.h"
|
|
||||||
|
|
||||||
#if !MESHTASTIC_EXCLUDE_I2C
|
#if !MESHTASTIC_EXCLUDE_I2C
|
||||||
#include "input/cardKbI2cImpl.h"
|
#include "input/cardKbI2cImpl.h"
|
||||||
#endif
|
#endif
|
||||||
@ -121,10 +119,6 @@ void setupModules()
|
|||||||
buzzerFeedbackThread = new BuzzerFeedbackThread();
|
buzzerFeedbackThread = new BuzzerFeedbackThread();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if defined(LED_CHARGE) || defined(LED_PAIRING)
|
|
||||||
statusLEDModule = new StatusLEDModule();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !MESHTASTIC_EXCLUDE_ADMIN
|
#if !MESHTASTIC_EXCLUDE_ADMIN
|
||||||
adminModule = new AdminModule();
|
adminModule = new AdminModule();
|
||||||
#endif
|
#endif
|
||||||
@ -181,25 +175,24 @@ void setupModules()
|
|||||||
// new ReplyModule();
|
// new ReplyModule();
|
||||||
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
|
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
|
||||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||||
#if defined(T_LORA_PAGER)
|
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
|
||||||
|
if (!rotaryEncoderInterruptImpl1->init()) {
|
||||||
|
delete rotaryEncoderInterruptImpl1;
|
||||||
|
rotaryEncoderInterruptImpl1 = nullptr;
|
||||||
|
}
|
||||||
|
#ifdef T_LORA_PAGER
|
||||||
// use a special FSM based rotary encoder version for T-LoRa Pager
|
// use a special FSM based rotary encoder version for T-LoRa Pager
|
||||||
rotaryEncoderImpl = new RotaryEncoderImpl();
|
rotaryEncoderImpl = new RotaryEncoderImpl();
|
||||||
if (!rotaryEncoderImpl->init()) {
|
if (!rotaryEncoderImpl->init()) {
|
||||||
delete rotaryEncoderImpl;
|
delete rotaryEncoderImpl;
|
||||||
rotaryEncoderImpl = nullptr;
|
rotaryEncoderImpl = nullptr;
|
||||||
}
|
}
|
||||||
#elif defined(INPUTDRIVER_ENCODER_TYPE) && (INPUTDRIVER_ENCODER_TYPE == 2)
|
#else
|
||||||
upDownInterruptImpl1 = new UpDownInterruptImpl1();
|
upDownInterruptImpl1 = new UpDownInterruptImpl1();
|
||||||
if (!upDownInterruptImpl1->init()) {
|
if (!upDownInterruptImpl1->init()) {
|
||||||
delete upDownInterruptImpl1;
|
delete upDownInterruptImpl1;
|
||||||
upDownInterruptImpl1 = nullptr;
|
upDownInterruptImpl1 = nullptr;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
|
|
||||||
if (!rotaryEncoderInterruptImpl1->init()) {
|
|
||||||
delete rotaryEncoderInterruptImpl1;
|
|
||||||
rotaryEncoderInterruptImpl1 = nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
cardKbI2cImpl = new CardKbI2cImpl();
|
cardKbI2cImpl = new CardKbI2cImpl();
|
||||||
cardKbI2cImpl->init();
|
cardKbI2cImpl->init();
|
||||||
|
|||||||
@ -1,272 +0,0 @@
|
|||||||
#include "configuration.h"
|
|
||||||
#if HAS_SCREEN
|
|
||||||
|
|
||||||
#include "graphics/SharedUIDisplay.h"
|
|
||||||
#include "graphics/draw/NotificationRenderer.h"
|
|
||||||
#include "input/RotaryEncoderInterruptImpl1.h"
|
|
||||||
#include "input/UpDownInterruptImpl1.h"
|
|
||||||
#include "modules/OnScreenKeyboardModule.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace graphics
|
|
||||||
{
|
|
||||||
|
|
||||||
OnScreenKeyboardModule &OnScreenKeyboardModule::instance()
|
|
||||||
{
|
|
||||||
static OnScreenKeyboardModule inst;
|
|
||||||
return inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnScreenKeyboardModule::~OnScreenKeyboardModule()
|
|
||||||
{
|
|
||||||
if (keyboard) {
|
|
||||||
delete keyboard;
|
|
||||||
keyboard = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnScreenKeyboardModule::start(const char *header, const char *initialText, uint32_t durationMs,
|
|
||||||
std::function<void(const std::string &)> cb)
|
|
||||||
{
|
|
||||||
if (keyboard) {
|
|
||||||
delete keyboard;
|
|
||||||
keyboard = nullptr;
|
|
||||||
}
|
|
||||||
keyboard = new VirtualKeyboard();
|
|
||||||
callback = cb;
|
|
||||||
if (header)
|
|
||||||
keyboard->setHeader(header);
|
|
||||||
if (initialText)
|
|
||||||
keyboard->setInputText(initialText);
|
|
||||||
|
|
||||||
// Route VK submission/cancel events back into the module
|
|
||||||
keyboard->setCallback([this](const std::string &text) {
|
|
||||||
if (text.empty()) {
|
|
||||||
this->onCancel();
|
|
||||||
} else {
|
|
||||||
this->onSubmit(text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Maintain legacy compatibility hooks
|
|
||||||
NotificationRenderer::virtualKeyboard = keyboard;
|
|
||||||
NotificationRenderer::textInputCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnScreenKeyboardModule::stop(bool callEmptyCallback)
|
|
||||||
{
|
|
||||||
auto cb = callback;
|
|
||||||
callback = nullptr;
|
|
||||||
if (keyboard) {
|
|
||||||
delete keyboard;
|
|
||||||
keyboard = nullptr;
|
|
||||||
}
|
|
||||||
// Keep NotificationRenderer legacy pointers in sync
|
|
||||||
NotificationRenderer::virtualKeyboard = nullptr;
|
|
||||||
NotificationRenderer::textInputCallback = nullptr;
|
|
||||||
clearPopup();
|
|
||||||
if (callEmptyCallback && cb)
|
|
||||||
cb("");
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnScreenKeyboardModule::handleInput(const InputEvent &event)
|
|
||||||
{
|
|
||||||
if (!keyboard)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (processVirtualKeyboardInput(event, keyboard))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (event.inputEvent == INPUT_BROKER_CANCEL)
|
|
||||||
onCancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OnScreenKeyboardModule::processVirtualKeyboardInput(const InputEvent &event, VirtualKeyboard *targetKeyboard)
|
|
||||||
{
|
|
||||||
if (!targetKeyboard)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
switch (event.inputEvent) {
|
|
||||||
case INPUT_BROKER_UP:
|
|
||||||
case INPUT_BROKER_UP_LONG:
|
|
||||||
targetKeyboard->moveCursorUp();
|
|
||||||
return true;
|
|
||||||
case INPUT_BROKER_DOWN:
|
|
||||||
case INPUT_BROKER_DOWN_LONG:
|
|
||||||
targetKeyboard->moveCursorDown();
|
|
||||||
return true;
|
|
||||||
case INPUT_BROKER_LEFT:
|
|
||||||
case INPUT_BROKER_ALT_PRESS:
|
|
||||||
targetKeyboard->moveCursorLeft();
|
|
||||||
return true;
|
|
||||||
case INPUT_BROKER_RIGHT:
|
|
||||||
case INPUT_BROKER_USER_PRESS:
|
|
||||||
targetKeyboard->moveCursorRight();
|
|
||||||
return true;
|
|
||||||
case INPUT_BROKER_SELECT:
|
|
||||||
targetKeyboard->handlePress();
|
|
||||||
return true;
|
|
||||||
case INPUT_BROKER_SELECT_LONG:
|
|
||||||
targetKeyboard->handleLongPress();
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OnScreenKeyboardModule::draw(OLEDDisplay *display)
|
|
||||||
{
|
|
||||||
if (!keyboard)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Timeout
|
|
||||||
if (keyboard->isTimedOut()) {
|
|
||||||
onCancel();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear full screen behind keyboard
|
|
||||||
display->setColor(BLACK);
|
|
||||||
display->fillRect(0, 0, display->getWidth(), display->getHeight());
|
|
||||||
display->setColor(WHITE);
|
|
||||||
keyboard->draw(display, 0, 0);
|
|
||||||
|
|
||||||
// Draw popup overlay if needed
|
|
||||||
drawPopup(display);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnScreenKeyboardModule::onSubmit(const std::string &text)
|
|
||||||
{
|
|
||||||
auto cb = callback;
|
|
||||||
stop(false);
|
|
||||||
if (cb)
|
|
||||||
cb(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnScreenKeyboardModule::onCancel()
|
|
||||||
{
|
|
||||||
stop(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnScreenKeyboardModule::showPopup(const char *title, const char *content, uint32_t durationMs)
|
|
||||||
{
|
|
||||||
if (!title || !content)
|
|
||||||
return;
|
|
||||||
strncpy(popupTitle, title, sizeof(popupTitle) - 1);
|
|
||||||
popupTitle[sizeof(popupTitle) - 1] = '\0';
|
|
||||||
strncpy(popupMessage, content, sizeof(popupMessage) - 1);
|
|
||||||
popupMessage[sizeof(popupMessage) - 1] = '\0';
|
|
||||||
popupUntil = millis() + durationMs;
|
|
||||||
popupVisible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnScreenKeyboardModule::clearPopup()
|
|
||||||
{
|
|
||||||
popupTitle[0] = '\0';
|
|
||||||
popupMessage[0] = '\0';
|
|
||||||
popupUntil = 0;
|
|
||||||
popupVisible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnScreenKeyboardModule::drawPopupOverlay(OLEDDisplay *display)
|
|
||||||
{
|
|
||||||
// Only render the popup overlay (without drawing the keyboard)
|
|
||||||
drawPopup(display);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnScreenKeyboardModule::drawPopup(OLEDDisplay *display)
|
|
||||||
{
|
|
||||||
if (!popupVisible)
|
|
||||||
return;
|
|
||||||
if (millis() > popupUntil || popupMessage[0] == '\0') {
|
|
||||||
popupVisible = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build lines and leverage NotificationRenderer inverted box drawing for consistent style
|
|
||||||
constexpr uint16_t maxContentLines = 3;
|
|
||||||
const bool hasTitle = popupTitle[0] != '\0';
|
|
||||||
|
|
||||||
display->setFont(FONT_SMALL);
|
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
||||||
const uint16_t maxWrapWidth = display->width() - 40;
|
|
||||||
|
|
||||||
auto wrapText = [&](const char *text, uint16_t availableWidth) -> std::vector<std::string> {
|
|
||||||
std::vector<std::string> wrapped;
|
|
||||||
std::string current;
|
|
||||||
std::string word;
|
|
||||||
const char *p = text;
|
|
||||||
while (*p && wrapped.size() < maxContentLines) {
|
|
||||||
while (*p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) {
|
|
||||||
if (*p == '\n') {
|
|
||||||
if (!current.empty()) {
|
|
||||||
wrapped.push_back(current);
|
|
||||||
current.clear();
|
|
||||||
if (wrapped.size() >= maxContentLines)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
if (!*p || wrapped.size() >= maxContentLines)
|
|
||||||
break;
|
|
||||||
word.clear();
|
|
||||||
while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r')
|
|
||||||
word += *p++;
|
|
||||||
if (word.empty())
|
|
||||||
continue;
|
|
||||||
std::string test = current.empty() ? word : (current + " " + word);
|
|
||||||
uint16_t w = display->getStringWidth(test.c_str(), test.length(), true);
|
|
||||||
if (w <= availableWidth)
|
|
||||||
current = test;
|
|
||||||
else {
|
|
||||||
if (!current.empty()) {
|
|
||||||
wrapped.push_back(current);
|
|
||||||
current = word;
|
|
||||||
if (wrapped.size() >= maxContentLines)
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
current = word;
|
|
||||||
while (current.size() > 1 &&
|
|
||||||
display->getStringWidth(current.c_str(), current.length(), true) > availableWidth)
|
|
||||||
current.pop_back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!current.empty() && wrapped.size() < maxContentLines)
|
|
||||||
wrapped.push_back(current);
|
|
||||||
return wrapped;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::string> allLines;
|
|
||||||
if (hasTitle)
|
|
||||||
allLines.emplace_back(popupTitle);
|
|
||||||
|
|
||||||
char buf[sizeof(popupMessage)];
|
|
||||||
strncpy(buf, popupMessage, sizeof(buf) - 1);
|
|
||||||
buf[sizeof(buf) - 1] = '\0';
|
|
||||||
char *paragraph = strtok(buf, "\n");
|
|
||||||
while (paragraph && allLines.size() < maxContentLines + (hasTitle ? 1 : 0)) {
|
|
||||||
auto wrapped = wrapText(paragraph, maxWrapWidth);
|
|
||||||
for (const auto &ln : wrapped) {
|
|
||||||
if (allLines.size() >= maxContentLines + (hasTitle ? 1 : 0))
|
|
||||||
break;
|
|
||||||
allLines.push_back(ln);
|
|
||||||
}
|
|
||||||
paragraph = strtok(nullptr, "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<const char *> ptrs;
|
|
||||||
for (const auto &ln : allLines)
|
|
||||||
ptrs.push_back(ln.c_str());
|
|
||||||
ptrs.push_back(nullptr);
|
|
||||||
|
|
||||||
// Use the standard notification box drawing from NotificationRenderer
|
|
||||||
NotificationRenderer::drawNotificationBox(display, nullptr, ptrs.data(), allLines.size(), 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace graphics
|
|
||||||
|
|
||||||
#endif // HAS_SCREEN
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "configuration.h"
|
|
||||||
#if HAS_SCREEN
|
|
||||||
|
|
||||||
#include "graphics/Screen.h" // InputEvent
|
|
||||||
#include "graphics/VirtualKeyboard.h"
|
|
||||||
#include <OLEDDisplay.h>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace graphics
|
|
||||||
{
|
|
||||||
class OnScreenKeyboardModule
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static OnScreenKeyboardModule &instance();
|
|
||||||
|
|
||||||
void start(const char *header, const char *initialText, uint32_t durationMs,
|
|
||||||
std::function<void(const std::string &)> callback);
|
|
||||||
|
|
||||||
void stop(bool callEmptyCallback);
|
|
||||||
|
|
||||||
void handleInput(const InputEvent &event);
|
|
||||||
static bool processVirtualKeyboardInput(const InputEvent &event, VirtualKeyboard *keyboard);
|
|
||||||
bool draw(OLEDDisplay *display);
|
|
||||||
|
|
||||||
void showPopup(const char *title, const char *content, uint32_t durationMs);
|
|
||||||
void clearPopup();
|
|
||||||
// Draw only the popup overlay (used when legacy virtualKeyboard draws the keyboard)
|
|
||||||
void drawPopupOverlay(OLEDDisplay *display);
|
|
||||||
|
|
||||||
private:
|
|
||||||
OnScreenKeyboardModule() = default;
|
|
||||||
~OnScreenKeyboardModule();
|
|
||||||
OnScreenKeyboardModule(const OnScreenKeyboardModule &) = delete;
|
|
||||||
OnScreenKeyboardModule &operator=(const OnScreenKeyboardModule &) = delete;
|
|
||||||
|
|
||||||
void onSubmit(const std::string &text);
|
|
||||||
void onCancel();
|
|
||||||
|
|
||||||
void drawPopup(OLEDDisplay *display);
|
|
||||||
|
|
||||||
VirtualKeyboard *keyboard = nullptr;
|
|
||||||
std::function<void(const std::string &)> callback;
|
|
||||||
|
|
||||||
char popupTitle[64] = {0};
|
|
||||||
char popupMessage[256] = {0};
|
|
||||||
uint32_t popupUntil = 0;
|
|
||||||
bool popupVisible = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace graphics
|
|
||||||
|
|
||||||
#endif // HAS_SCREEN
|
|
||||||
@ -64,8 +64,7 @@ SerialModule *serialModule;
|
|||||||
SerialModuleRadio *serialModuleRadio;
|
SerialModuleRadio *serialModuleRadio;
|
||||||
|
|
||||||
#if defined(TTGO_T_ECHO) || defined(CANARYONE) || defined(MESHLINK) || defined(ELECROW_ThinkNode_M1) || \
|
#if defined(TTGO_T_ECHO) || defined(CANARYONE) || defined(MESHLINK) || defined(ELECROW_ThinkNode_M1) || \
|
||||||
defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE) || defined(ELECROW_ThinkNode_M3) || \
|
defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE)
|
||||||
defined(MUZI_BASE)
|
|
||||||
SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial")
|
SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial")
|
||||||
{
|
{
|
||||||
api_type = TYPE_SERIAL;
|
api_type = TYPE_SERIAL;
|
||||||
@ -205,7 +204,7 @@ int32_t SerialModule::runOnce()
|
|||||||
Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT);
|
Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT);
|
||||||
}
|
}
|
||||||
#elif !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(MESHLINK) && \
|
#elif !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(MESHLINK) && \
|
||||||
!defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M5) && !defined(MUZI_BASE)
|
!defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5)
|
||||||
if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
|
if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
|
||||||
#ifdef ARCH_RP2040
|
#ifdef ARCH_RP2040
|
||||||
Serial2.setFIFOSize(RX_BUFFER);
|
Serial2.setFIFOSize(RX_BUFFER);
|
||||||
@ -262,7 +261,7 @@ int32_t SerialModule::runOnce()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(MESHLINK) && \
|
#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(MESHLINK) && \
|
||||||
!defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M5) && !defined(MUZI_BASE)
|
!defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5)
|
||||||
else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85)) {
|
else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85)) {
|
||||||
processWXSerial();
|
processWXSerial();
|
||||||
|
|
||||||
@ -537,8 +536,7 @@ ParsedLine parseLine(const char *line)
|
|||||||
void SerialModule::processWXSerial()
|
void SerialModule::processWXSerial()
|
||||||
{
|
{
|
||||||
#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && \
|
#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && \
|
||||||
!defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M5) && \
|
!defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M5) && !defined(ARCH_STM32WL)
|
||||||
!defined(ARCH_STM32WL) && !defined(MUZI_BASE)
|
|
||||||
static unsigned int lastAveraged = 0;
|
static unsigned int lastAveraged = 0;
|
||||||
static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded.
|
static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded.
|
||||||
static double dir_sum_sin = 0;
|
static double dir_sum_sin = 0;
|
||||||
|
|||||||
@ -1,94 +0,0 @@
|
|||||||
#include "StatusLEDModule.h"
|
|
||||||
#include "MeshService.h"
|
|
||||||
#include "configuration.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
StatusLEDModule manages the device's status LEDs, updating their states based on power and Bluetooth status.
|
|
||||||
It reflects charging, charged, discharging, and Bluetooth connection states using the appropriate LEDs.
|
|
||||||
*/
|
|
||||||
StatusLEDModule *statusLEDModule;
|
|
||||||
|
|
||||||
StatusLEDModule::StatusLEDModule() : concurrency::OSThread("StatusLEDModule")
|
|
||||||
{
|
|
||||||
bluetoothStatusObserver.observe(&bluetoothStatus->onNewStatus);
|
|
||||||
powerStatusObserver.observe(&powerStatus->onNewStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
|
|
||||||
{
|
|
||||||
switch (arg->getStatusType()) {
|
|
||||||
case STATUS_TYPE_POWER: {
|
|
||||||
meshtastic::PowerStatus *powerStatus = (meshtastic::PowerStatus *)arg;
|
|
||||||
if (powerStatus->getHasUSB()) {
|
|
||||||
power_state = charging;
|
|
||||||
if (powerStatus->getBatteryChargePercent() >= 100) {
|
|
||||||
power_state = charged;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
power_state = discharging;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case STATUS_TYPE_BLUETOOTH: {
|
|
||||||
meshtastic::BluetoothStatus *bluetoothStatus = (meshtastic::BluetoothStatus *)arg;
|
|
||||||
switch (bluetoothStatus->getConnectionState()) {
|
|
||||||
case meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED: {
|
|
||||||
ble_state = unpaired;
|
|
||||||
PAIRING_LED_starttime = millis();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case meshtastic::BluetoothStatus::ConnectionState::PAIRING: {
|
|
||||||
ble_state = pairing;
|
|
||||||
PAIRING_LED_starttime = millis();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case meshtastic::BluetoothStatus::ConnectionState::CONNECTED: {
|
|
||||||
ble_state = connected;
|
|
||||||
PAIRING_LED_starttime = millis();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
int32_t StatusLEDModule::runOnce()
|
|
||||||
{
|
|
||||||
|
|
||||||
if (power_state == charging) {
|
|
||||||
CHARGE_LED_state = !CHARGE_LED_state;
|
|
||||||
} else if (power_state == charged) {
|
|
||||||
CHARGE_LED_state = LED_STATE_ON;
|
|
||||||
} else {
|
|
||||||
CHARGE_LED_state = LED_STATE_OFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.bluetooth.enabled || PAIRING_LED_starttime + 30 * 1000 < millis()) {
|
|
||||||
PAIRING_LED_state = LED_STATE_OFF;
|
|
||||||
} else if (ble_state == unpaired) {
|
|
||||||
if (slowTrack) {
|
|
||||||
PAIRING_LED_state = !PAIRING_LED_state;
|
|
||||||
slowTrack = false;
|
|
||||||
} else {
|
|
||||||
slowTrack = true;
|
|
||||||
}
|
|
||||||
} else if (ble_state == pairing) {
|
|
||||||
PAIRING_LED_state = !PAIRING_LED_state;
|
|
||||||
} else {
|
|
||||||
PAIRING_LED_state = LED_STATE_ON;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef LED_CHARGE
|
|
||||||
digitalWrite(LED_CHARGE, CHARGE_LED_state);
|
|
||||||
#endif
|
|
||||||
// digitalWrite(green_LED_PIN, LED_STATE_OFF);
|
|
||||||
#ifdef LED_PAIRING
|
|
||||||
digitalWrite(LED_PAIRING, PAIRING_LED_state);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return (my_interval);
|
|
||||||
}
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "BluetoothStatus.h"
|
|
||||||
#include "MeshModule.h"
|
|
||||||
#include "PowerStatus.h"
|
|
||||||
#include "concurrency/OSThread.h"
|
|
||||||
#include "configuration.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
class StatusLEDModule : private concurrency::OSThread
|
|
||||||
{
|
|
||||||
bool slowTrack = false;
|
|
||||||
|
|
||||||
public:
|
|
||||||
StatusLEDModule();
|
|
||||||
|
|
||||||
int handleStatusUpdate(const meshtastic::Status *);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
unsigned int my_interval = 1000; // interval in millisconds
|
|
||||||
virtual int32_t runOnce() override;
|
|
||||||
|
|
||||||
CallbackObserver<StatusLEDModule, const meshtastic::Status *> bluetoothStatusObserver =
|
|
||||||
CallbackObserver<StatusLEDModule, const meshtastic::Status *>(this, &StatusLEDModule::handleStatusUpdate);
|
|
||||||
CallbackObserver<StatusLEDModule, const meshtastic::Status *> powerStatusObserver =
|
|
||||||
CallbackObserver<StatusLEDModule, const meshtastic::Status *>(this, &StatusLEDModule::handleStatusUpdate);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool CHARGE_LED_state = LED_STATE_OFF;
|
|
||||||
bool PAIRING_LED_state = LED_STATE_OFF;
|
|
||||||
|
|
||||||
uint32_t PAIRING_LED_starttime = 0;
|
|
||||||
|
|
||||||
enum PowerState { discharging, charging, charged };
|
|
||||||
|
|
||||||
PowerState power_state = discharging;
|
|
||||||
|
|
||||||
enum BLEState { unpaired, pairing, connected };
|
|
||||||
|
|
||||||
BLEState ble_state = unpaired;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern StatusLEDModule *statusLEDModule;
|
|
||||||
@ -1,5 +1,4 @@
|
|||||||
#include "SystemCommandsModule.h"
|
#include "SystemCommandsModule.h"
|
||||||
#include "input/InputBroker.h"
|
|
||||||
#include "meshUtils.h"
|
#include "meshUtils.h"
|
||||||
#if HAS_SCREEN
|
#if HAS_SCREEN
|
||||||
#include "graphics/Screen.h"
|
#include "graphics/Screen.h"
|
||||||
@ -23,7 +22,7 @@ SystemCommandsModule::SystemCommandsModule()
|
|||||||
|
|
||||||
int SystemCommandsModule::handleInputEvent(const InputEvent *event)
|
int SystemCommandsModule::handleInputEvent(const InputEvent *event)
|
||||||
{
|
{
|
||||||
LOG_INPUT("SystemCommands Input event %u! kb %u", event->inputEvent, event->kbchar);
|
LOG_INFO("Input event %u! kb %u", event->inputEvent, event->kbchar);
|
||||||
// System commands (all others fall through)
|
// System commands (all others fall through)
|
||||||
switch (event->kbchar) {
|
switch (event->kbchar) {
|
||||||
// Fn key symbols
|
// Fn key symbols
|
||||||
@ -86,8 +85,10 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event)
|
|||||||
switch (event->inputEvent) {
|
switch (event->inputEvent) {
|
||||||
// GPS
|
// GPS
|
||||||
case INPUT_BROKER_GPS_TOGGLE:
|
case INPUT_BROKER_GPS_TOGGLE:
|
||||||
|
LOG_WARN("GPS Toggle");
|
||||||
#if !MESHTASTIC_EXCLUDE_GPS
|
#if !MESHTASTIC_EXCLUDE_GPS
|
||||||
if (gps) {
|
if (gps) {
|
||||||
|
LOG_WARN("GPS Toggle2");
|
||||||
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED &&
|
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED &&
|
||||||
config.position.fixed_position == false) {
|
config.position.fixed_position == false) {
|
||||||
nodeDB->clearLocalPosition();
|
nodeDB->clearLocalPosition();
|
||||||
|
|||||||
@ -35,7 +35,7 @@ bool AHT10Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
|||||||
// prefer other sensors like bmp280, bmp3xx
|
// prefer other sensors like bmp280, bmp3xx
|
||||||
if (!measurement->variant.environment_metrics.has_temperature) {
|
if (!measurement->variant.environment_metrics.has_temperature) {
|
||||||
measurement->variant.environment_metrics.has_temperature = true;
|
measurement->variant.environment_metrics.has_temperature = true;
|
||||||
measurement->variant.environment_metrics.temperature = temp.temperature + AHT10_TEMP_OFFSET;
|
measurement->variant.environment_metrics.temperature = temp.temperature;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!measurement->variant.environment_metrics.has_relative_humidity) {
|
if (!measurement->variant.environment_metrics.has_relative_humidity) {
|
||||||
|
|||||||
@ -6,10 +6,6 @@
|
|||||||
|
|
||||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<Adafruit_AHTX0.h>)
|
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<Adafruit_AHTX0.h>)
|
||||||
|
|
||||||
#ifndef AHT10_TEMP_OFFSET
|
|
||||||
#define AHT10_TEMP_OFFSET 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||||
#include "TelemetrySensor.h"
|
#include "TelemetrySensor.h"
|
||||||
#include <Adafruit_AHTX0.h>
|
#include <Adafruit_AHTX0.h>
|
||||||
|
|||||||
@ -13,10 +13,7 @@ DFRobotGravitySensor::DFRobotGravitySensor() : TelemetrySensor(meshtastic_Teleme
|
|||||||
DFRobotGravitySensor::~DFRobotGravitySensor()
|
DFRobotGravitySensor::~DFRobotGravitySensor()
|
||||||
{
|
{
|
||||||
if (gravity) {
|
if (gravity) {
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
|
|
||||||
delete gravity;
|
delete gravity;
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
gravity = nullptr;
|
gravity = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,113 +11,6 @@ extern graphics::Screen *screen;
|
|||||||
|
|
||||||
TraceRouteModule *traceRouteModule;
|
TraceRouteModule *traceRouteModule;
|
||||||
|
|
||||||
void TraceRouteModule::setResultText(const String &text)
|
|
||||||
{
|
|
||||||
resultText = text;
|
|
||||||
resultLines.clear();
|
|
||||||
resultLinesDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TraceRouteModule::clearResultLines()
|
|
||||||
{
|
|
||||||
resultLines.clear();
|
|
||||||
resultLinesDirty = false;
|
|
||||||
}
|
|
||||||
#if HAS_SCREEN
|
|
||||||
void TraceRouteModule::rebuildResultLines(OLEDDisplay *display)
|
|
||||||
{
|
|
||||||
if (!display) {
|
|
||||||
resultLinesDirty = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
resultLines.clear();
|
|
||||||
|
|
||||||
if (resultText.length() == 0) {
|
|
||||||
resultLinesDirty = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int maxWidth = display->getWidth() - 4;
|
|
||||||
if (maxWidth <= 0) {
|
|
||||||
resultLinesDirty = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = 0;
|
|
||||||
int textLength = resultText.length();
|
|
||||||
|
|
||||||
while (start <= textLength) {
|
|
||||||
int newlinePos = resultText.indexOf('\n', start);
|
|
||||||
String segment;
|
|
||||||
|
|
||||||
if (newlinePos != -1) {
|
|
||||||
segment = resultText.substring(start, newlinePos);
|
|
||||||
start = newlinePos + 1;
|
|
||||||
} else {
|
|
||||||
segment = resultText.substring(start);
|
|
||||||
start = textLength + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (segment.length() == 0) {
|
|
||||||
resultLines.push_back("");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (display->getStringWidth(segment) <= maxWidth) {
|
|
||||||
resultLines.push_back(segment);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
String remaining = segment;
|
|
||||||
|
|
||||||
while (remaining.length() > 0) {
|
|
||||||
String tempLine = "";
|
|
||||||
int lastGoodBreak = -1;
|
|
||||||
bool lineComplete = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < static_cast<int>(remaining.length()); i++) {
|
|
||||||
char ch = remaining.charAt(i);
|
|
||||||
String testLine = tempLine + ch;
|
|
||||||
|
|
||||||
if (display->getStringWidth(testLine) > maxWidth) {
|
|
||||||
if (lastGoodBreak >= 0) {
|
|
||||||
resultLines.push_back(remaining.substring(0, lastGoodBreak + 1));
|
|
||||||
remaining = remaining.substring(lastGoodBreak + 1);
|
|
||||||
lineComplete = true;
|
|
||||||
break;
|
|
||||||
} else if (tempLine.length() > 0) {
|
|
||||||
resultLines.push_back(tempLine);
|
|
||||||
remaining = remaining.substring(i);
|
|
||||||
lineComplete = true;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
resultLines.push_back(String(ch));
|
|
||||||
remaining = remaining.substring(i + 1);
|
|
||||||
lineComplete = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tempLine = testLine;
|
|
||||||
if (ch == ' ' || ch == '>' || ch == '<' || ch == '-' || ch == '(' || ch == ')' || ch == ',') {
|
|
||||||
lastGoodBreak = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lineComplete) {
|
|
||||||
if (tempLine.length() > 0) {
|
|
||||||
resultLines.push_back(tempLine);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resultLinesDirty = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool TraceRouteModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_RouteDiscovery *r)
|
bool TraceRouteModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_RouteDiscovery *r)
|
||||||
{
|
{
|
||||||
// We only alter the packet in alterReceivedProtobuf()
|
// We only alter the packet in alterReceivedProtobuf()
|
||||||
@ -513,7 +406,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
|||||||
if (node == 0 || node == NODENUM_BROADCAST) {
|
if (node == 0 || node == NODENUM_BROADCAST) {
|
||||||
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
|
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
setResultText("Invalid node");
|
resultText = "Invalid node";
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@ -527,7 +420,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
|||||||
if (node == nodeDB->getNodeNum()) {
|
if (node == nodeDB->getNodeNum()) {
|
||||||
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
|
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
setResultText("Cannot trace self");
|
resultText = "Cannot trace self";
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@ -554,8 +447,6 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
|||||||
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
|
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
|
||||||
bannerText = String("Wait for ") + String(wait) + String("s");
|
bannerText = String("Wait for ") + String(wait) + String("s");
|
||||||
runState = TRACEROUTE_STATE_COOLDOWN;
|
runState = TRACEROUTE_STATE_COOLDOWN;
|
||||||
resultText = "";
|
|
||||||
clearResultLines();
|
|
||||||
|
|
||||||
requestFocus();
|
requestFocus();
|
||||||
UIFrameEvent e;
|
UIFrameEvent e;
|
||||||
@ -568,8 +459,6 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
|||||||
tracingNode = node;
|
tracingNode = node;
|
||||||
lastTraceRouteTime = now;
|
lastTraceRouteTime = now;
|
||||||
runState = TRACEROUTE_STATE_TRACKING;
|
runState = TRACEROUTE_STATE_TRACKING;
|
||||||
resultText = "";
|
|
||||||
clearResultLines();
|
|
||||||
bannerText = String("Tracing ") + getNodeName(node);
|
bannerText = String("Tracing ") + getNodeName(node);
|
||||||
|
|
||||||
LOG_INFO("TraceRoute UI: Starting trace route to node 0x%08x, requesting focus", node);
|
LOG_INFO("TraceRoute UI: Starting trace route to node 0x%08x, requesting focus", node);
|
||||||
@ -612,7 +501,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
|||||||
} else {
|
} else {
|
||||||
LOG_ERROR("MeshService is NULL!");
|
LOG_ERROR("MeshService is NULL!");
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
setResultText("Service unavailable");
|
resultText = "Service unavailable";
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@ -625,7 +514,7 @@ bool TraceRouteModule::startTraceRoute(NodeNum node)
|
|||||||
} else {
|
} else {
|
||||||
LOG_ERROR("Failed to allocate TraceRoute packet from router");
|
LOG_ERROR("Failed to allocate TraceRoute packet from router");
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
setResultText("Failed to send");
|
resultText = "Failed to send";
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@ -643,7 +532,7 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
if (node == 0 || node == NODENUM_BROADCAST) {
|
if (node == 0 || node == NODENUM_BROADCAST) {
|
||||||
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
|
LOG_ERROR("Invalid node number for trace route: 0x%08x", node);
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
setResultText("Invalid node");
|
resultText = "Invalid node";
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@ -657,7 +546,7 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
if (node == nodeDB->getNodeNum()) {
|
if (node == nodeDB->getNodeNum()) {
|
||||||
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
|
LOG_ERROR("Cannot trace route to self: 0x%08x", node);
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
setResultText("Cannot trace self");
|
resultText = "Cannot trace self";
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@ -679,8 +568,6 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
|
unsigned long wait = (cooldownMs - (now - lastTraceRouteTime)) / 1000;
|
||||||
bannerText = String("Wait for ") + String(wait) + String("s");
|
bannerText = String("Wait for ") + String(wait) + String("s");
|
||||||
runState = TRACEROUTE_STATE_COOLDOWN;
|
runState = TRACEROUTE_STATE_COOLDOWN;
|
||||||
resultText = "";
|
|
||||||
clearResultLines();
|
|
||||||
|
|
||||||
requestFocus();
|
requestFocus();
|
||||||
UIFrameEvent e;
|
UIFrameEvent e;
|
||||||
@ -693,8 +580,6 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
runState = TRACEROUTE_STATE_TRACKING;
|
runState = TRACEROUTE_STATE_TRACKING;
|
||||||
tracingNode = node;
|
tracingNode = node;
|
||||||
lastTraceRouteTime = now;
|
lastTraceRouteTime = now;
|
||||||
resultText = "";
|
|
||||||
clearResultLines();
|
|
||||||
bannerText = String("Tracing ") + getNodeName(node);
|
bannerText = String("Tracing ") + getNodeName(node);
|
||||||
|
|
||||||
requestFocus();
|
requestFocus();
|
||||||
@ -729,14 +614,14 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
} else {
|
} else {
|
||||||
LOG_ERROR("MeshService is NULL!");
|
LOG_ERROR("MeshService is NULL!");
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
setResultText("Service unavailable");
|
resultText = "Service unavailable";
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR("Failed to allocate TraceRoute packet from router");
|
LOG_ERROR("Failed to allocate TraceRoute packet from router");
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
setResultText("Failed to send");
|
resultText = "Failed to send";
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
}
|
}
|
||||||
@ -744,7 +629,7 @@ void TraceRouteModule::launch(NodeNum node)
|
|||||||
|
|
||||||
void TraceRouteModule::handleTraceRouteResult(const String &result)
|
void TraceRouteModule::handleTraceRouteResult(const String &result)
|
||||||
{
|
{
|
||||||
setResultText(result);
|
resultText = result;
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
resultShowTime = millis();
|
resultShowTime = millis();
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
@ -794,15 +679,83 @@ void TraceRouteModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
|||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
|
|
||||||
if (resultText.length() > 0) {
|
if (resultText.length() > 0) {
|
||||||
if (resultLinesDirty) {
|
std::vector<String> lines;
|
||||||
rebuildResultLines(display);
|
String currentLine = "";
|
||||||
|
int maxWidth = display->getWidth() - 4;
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
int newlinePos = resultText.indexOf('\n', start);
|
||||||
|
|
||||||
|
while (newlinePos != -1 || start < static_cast<int>(resultText.length())) {
|
||||||
|
String segment;
|
||||||
|
if (newlinePos != -1) {
|
||||||
|
segment = resultText.substring(start, newlinePos);
|
||||||
|
start = newlinePos + 1;
|
||||||
|
newlinePos = resultText.indexOf('\n', start);
|
||||||
|
} else {
|
||||||
|
segment = resultText.substring(start);
|
||||||
|
start = resultText.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (display->getStringWidth(segment) <= maxWidth) {
|
||||||
|
lines.push_back(segment);
|
||||||
|
} else {
|
||||||
|
// Try to break at better positions (space, >, <, -)
|
||||||
|
String remaining = segment;
|
||||||
|
|
||||||
|
while (remaining.length() > 0) {
|
||||||
|
String tempLine = "";
|
||||||
|
int lastGoodBreak = -1;
|
||||||
|
bool lineComplete = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < static_cast<int>(remaining.length()); i++) {
|
||||||
|
char ch = remaining.charAt(i);
|
||||||
|
String testLine = tempLine + ch;
|
||||||
|
|
||||||
|
if (display->getStringWidth(testLine) > maxWidth) {
|
||||||
|
if (lastGoodBreak >= 0) {
|
||||||
|
// Break at the last good position
|
||||||
|
lines.push_back(remaining.substring(0, lastGoodBreak + 1));
|
||||||
|
remaining = remaining.substring(lastGoodBreak + 1);
|
||||||
|
lineComplete = true;
|
||||||
|
break;
|
||||||
|
} else if (tempLine.length() > 0) {
|
||||||
|
lines.push_back(tempLine);
|
||||||
|
remaining = remaining.substring(i);
|
||||||
|
lineComplete = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// Single character exceeds width
|
||||||
|
lines.push_back(String(ch));
|
||||||
|
remaining = remaining.substring(i + 1);
|
||||||
|
lineComplete = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tempLine = testLine;
|
||||||
|
// Mark good break positions
|
||||||
|
if (ch == ' ' || ch == '>' || ch == '<' || ch == '-' || ch == '(' || ch == ')') {
|
||||||
|
lastGoodBreak = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lineComplete) {
|
||||||
|
// Reached end of remaining text
|
||||||
|
if (tempLine.length() > 0) {
|
||||||
|
lines.push_back(tempLine);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int lineHeight = FONT_HEIGHT_SMALL + 1; // Use proper font height with 1px spacing
|
int lineHeight = FONT_HEIGHT_SMALL + 1; // Use proper font height with 1px spacing
|
||||||
for (size_t i = 0; i < resultLines.size(); i++) {
|
for (size_t i = 0; i < lines.size(); i++) {
|
||||||
int lineY = contentStartY + (i * lineHeight);
|
int lineY = contentStartY + (i * lineHeight);
|
||||||
if (lineY + FONT_HEIGHT_SMALL <= display->getHeight()) {
|
if (lineY + FONT_HEIGHT_SMALL <= display->getHeight()) {
|
||||||
display->drawString(x + 2, lineY, resultLines[i]);
|
display->drawString(x + 2, lineY, lines[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -826,7 +779,7 @@ int32_t TraceRouteModule::runOnce()
|
|||||||
if (runState == TRACEROUTE_STATE_TRACKING && now - lastTraceRouteTime > trackingTimeoutMs) {
|
if (runState == TRACEROUTE_STATE_TRACKING && now - lastTraceRouteTime > trackingTimeoutMs) {
|
||||||
LOG_INFO("TraceRoute timeout, no response received");
|
LOG_INFO("TraceRoute timeout, no response received");
|
||||||
runState = TRACEROUTE_STATE_RESULT;
|
runState = TRACEROUTE_STATE_RESULT;
|
||||||
setResultText("No response received");
|
resultText = "No response received";
|
||||||
resultShowTime = now;
|
resultShowTime = now;
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
|
|
||||||
@ -862,8 +815,6 @@ int32_t TraceRouteModule::runOnce()
|
|||||||
// Cooldown finished
|
// Cooldown finished
|
||||||
LOG_INFO("TraceRoute cooldown finished, returning to IDLE");
|
LOG_INFO("TraceRoute cooldown finished, returning to IDLE");
|
||||||
runState = TRACEROUTE_STATE_IDLE;
|
runState = TRACEROUTE_STATE_IDLE;
|
||||||
resultText = "";
|
|
||||||
clearResultLines();
|
|
||||||
bannerText = "";
|
bannerText = "";
|
||||||
UIFrameEvent e;
|
UIFrameEvent e;
|
||||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||||
@ -877,7 +828,6 @@ int32_t TraceRouteModule::runOnce()
|
|||||||
LOG_INFO("TraceRoute result display timeout, returning to IDLE");
|
LOG_INFO("TraceRoute result display timeout, returning to IDLE");
|
||||||
runState = TRACEROUTE_STATE_IDLE;
|
runState = TRACEROUTE_STATE_IDLE;
|
||||||
resultText = "";
|
resultText = "";
|
||||||
clearResultLines();
|
|
||||||
bannerText = "";
|
bannerText = "";
|
||||||
tracingNode = 0;
|
tracingNode = 0;
|
||||||
UIFrameEvent e;
|
UIFrameEvent e;
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
#if HAS_SCREEN
|
#if HAS_SCREEN
|
||||||
#include "OLEDDisplayUi.h"
|
#include "OLEDDisplayUi.h"
|
||||||
#endif
|
#endif
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#define ROUTE_SIZE sizeof(((meshtastic_RouteDiscovery *)0)->route) / sizeof(((meshtastic_RouteDiscovery *)0)->route[0])
|
#define ROUTE_SIZE sizeof(((meshtastic_RouteDiscovery *)0)->route) / sizeof(((meshtastic_RouteDiscovery *)0)->route[0])
|
||||||
|
|
||||||
@ -50,11 +49,6 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>,
|
|||||||
virtual int32_t runOnce() override;
|
virtual int32_t runOnce() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setResultText(const String &text);
|
|
||||||
void clearResultLines();
|
|
||||||
#if HAS_SCREEN
|
|
||||||
void rebuildResultLines(OLEDDisplay *display);
|
|
||||||
#endif
|
|
||||||
// Call to add unknown hops (e.g. when a node couldn't decrypt it) to the route based on hopStart and current hopLimit
|
// Call to add unknown hops (e.g. when a node couldn't decrypt it) to the route based on hopStart and current hopLimit
|
||||||
void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination);
|
void insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_RouteDiscovery *r, bool isTowardsDestination);
|
||||||
|
|
||||||
@ -80,8 +74,6 @@ class TraceRouteModule : public ProtobufModule<meshtastic_RouteDiscovery>,
|
|||||||
unsigned long trackingTimeoutMs = 10000;
|
unsigned long trackingTimeoutMs = 10000;
|
||||||
String bannerText;
|
String bannerText;
|
||||||
String resultText;
|
String resultText;
|
||||||
std::vector<String> resultLines;
|
|
||||||
bool resultLinesDirty = false;
|
|
||||||
NodeNum tracingNode = 0;
|
NodeNum tracingNode = 0;
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -115,13 +115,7 @@ int32_t BMX160Sensor::runOnce()
|
|||||||
void BMX160Sensor::calibrate(uint16_t forSeconds)
|
void BMX160Sensor::calibrate(uint16_t forSeconds)
|
||||||
{
|
{
|
||||||
#if !defined(MESHTASTIC_EXCLUDE_SCREEN)
|
#if !defined(MESHTASTIC_EXCLUDE_SCREEN)
|
||||||
sBmx160SensorData_t magAccel;
|
|
||||||
sBmx160SensorData_t gAccel;
|
|
||||||
LOG_DEBUG("BMX160 calibration started for %is", forSeconds);
|
LOG_DEBUG("BMX160 calibration started for %is", forSeconds);
|
||||||
sensor.getAllData(&magAccel, NULL, &gAccel);
|
|
||||||
highestX = magAccel.x, lowestX = magAccel.x;
|
|
||||||
highestY = magAccel.y, lowestY = magAccel.y;
|
|
||||||
highestZ = magAccel.z, lowestZ = magAccel.z;
|
|
||||||
|
|
||||||
doCalibration = true;
|
doCalibration = true;
|
||||||
uint16_t calibrateFor = forSeconds * 1000; // calibrate for seconds provided
|
uint16_t calibrateFor = forSeconds * 1000; // calibrate for seconds provided
|
||||||
|
|||||||
@ -47,21 +47,6 @@ int32_t ICM20948Sensor::runOnce()
|
|||||||
int32_t ICM20948Sensor::runOnce()
|
int32_t ICM20948Sensor::runOnce()
|
||||||
{
|
{
|
||||||
#if !defined(MESHTASTIC_EXCLUDE_SCREEN) && HAS_SCREEN
|
#if !defined(MESHTASTIC_EXCLUDE_SCREEN) && HAS_SCREEN
|
||||||
#if defined(MUZI_BASE) // temporarily gated to single device due to feature freeze
|
|
||||||
if (screen && !screen->isScreenOn() && !config.display.wake_on_tap_or_motion && !config.device.double_tap_as_button_press) {
|
|
||||||
if (!isAsleep) {
|
|
||||||
LOG_DEBUG("sleeping IMU");
|
|
||||||
sensor->sleep(true);
|
|
||||||
isAsleep = true;
|
|
||||||
}
|
|
||||||
return MOTION_SENSOR_CHECK_INTERVAL_MS;
|
|
||||||
}
|
|
||||||
if (isAsleep) {
|
|
||||||
sensor->sleep(false);
|
|
||||||
isAsleep = false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
float magX = 0, magY = 0, magZ = 0;
|
float magX = 0, magY = 0, magZ = 0;
|
||||||
if (sensor->dataReady()) {
|
if (sensor->dataReady()) {
|
||||||
sensor->getAGMT();
|
sensor->getAGMT();
|
||||||
@ -171,20 +156,7 @@ int32_t ICM20948Sensor::runOnce()
|
|||||||
void ICM20948Sensor::calibrate(uint16_t forSeconds)
|
void ICM20948Sensor::calibrate(uint16_t forSeconds)
|
||||||
{
|
{
|
||||||
#if !defined(MESHTASTIC_EXCLUDE_SCREEN) && HAS_SCREEN
|
#if !defined(MESHTASTIC_EXCLUDE_SCREEN) && HAS_SCREEN
|
||||||
LOG_DEBUG("Old calibration data: highestX = %f, lowestX = %f, highestY = %f, lowestY = %f, highestZ = %f, lowestZ = %f",
|
|
||||||
highestX, lowestX, highestY, lowestY, highestZ, lowestZ);
|
|
||||||
LOG_DEBUG("BMX160 calibration started for %is", forSeconds);
|
LOG_DEBUG("BMX160 calibration started for %is", forSeconds);
|
||||||
if (sensor->dataReady()) {
|
|
||||||
sensor->getAGMT();
|
|
||||||
highestX = sensor->agmt.mag.axes.x;
|
|
||||||
lowestX = sensor->agmt.mag.axes.x;
|
|
||||||
highestY = sensor->agmt.mag.axes.y;
|
|
||||||
lowestY = sensor->agmt.mag.axes.y;
|
|
||||||
highestZ = sensor->agmt.mag.axes.z;
|
|
||||||
lowestZ = sensor->agmt.mag.axes.z;
|
|
||||||
} else {
|
|
||||||
highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
doCalibration = true;
|
doCalibration = true;
|
||||||
uint16_t calibrateFor = forSeconds * 1000; // calibrate for seconds provided
|
uint16_t calibrateFor = forSeconds * 1000; // calibrate for seconds provided
|
||||||
|
|||||||
@ -82,13 +82,7 @@ class ICM20948Sensor : public MotionSensor
|
|||||||
private:
|
private:
|
||||||
ICM20948Singleton *sensor = nullptr;
|
ICM20948Singleton *sensor = nullptr;
|
||||||
bool showingScreen = false;
|
bool showingScreen = false;
|
||||||
#ifdef MUZI_BASE
|
|
||||||
bool isAsleep = false;
|
|
||||||
float highestX = 449.000000, lowestX = -140.000000, highestY = 422.000000, lowestY = -232.000000, highestZ = 749.000000,
|
|
||||||
lowestZ = 98.000000;
|
|
||||||
#else
|
|
||||||
float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
|
float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ICM20948Sensor(ScanI2C::FoundDevice foundDevice);
|
explicit ICM20948Sensor(ScanI2C::FoundDevice foundDevice);
|
||||||
|
|||||||
@ -69,7 +69,6 @@ void MotionSensor::wakeScreen()
|
|||||||
{
|
{
|
||||||
if (powerFSM.getState() == &stateDARK) {
|
if (powerFSM.getState() == &stateDARK) {
|
||||||
LOG_DEBUG("Motion wakeScreen detected");
|
LOG_DEBUG("Motion wakeScreen detected");
|
||||||
if (config.display.wake_on_tap_or_motion)
|
|
||||||
powerFSM.trigger(EVENT_INPUT);
|
powerFSM.trigger(EVENT_INPUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -692,10 +692,7 @@ void MQTT::publishNodeInfo()
|
|||||||
}
|
}
|
||||||
void MQTT::publishQueuedMessages()
|
void MQTT::publishQueuedMessages()
|
||||||
{
|
{
|
||||||
if (mqttQueue.isEmpty())
|
if (mqttQueue.isEmpty() || !isConnected)
|
||||||
return;
|
|
||||||
|
|
||||||
if (!moduleConfig.mqtt.proxy_to_client_enabled && !isConnected)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LOG_DEBUG("Publish enqueued MQTT message");
|
LOG_DEBUG("Publish enqueued MQTT message");
|
||||||
|
|||||||
@ -20,14 +20,13 @@
|
|||||||
#include "PowerStatus.h"
|
#include "PowerStatus.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||||
#include "host/ble_gap.h"
|
#include "host/ble_gap.h"
|
||||||
#else
|
#else
|
||||||
#include "nimble/nimble/host/include/host/ble_gap.h"
|
#include "nimble/nimble/host/include/host/ble_gap.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr uint16_t kPreferredBleMtu = 517;
|
constexpr uint16_t kPreferredBleMtu = 517;
|
||||||
@ -777,35 +776,16 @@ bool NimbleBluetooth::isConnected()
|
|||||||
|
|
||||||
int NimbleBluetooth::getRssi()
|
int NimbleBluetooth::getRssi()
|
||||||
{
|
{
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
if (bleServer && isConnected()) {
|
||||||
if (!bleServer || !isConnected()) {
|
auto service = bleServer->getServiceByUUID(MESH_SERVICE_UUID);
|
||||||
return 0; // No active BLE connection
|
uint16_t handle = service->getHandle();
|
||||||
}
|
#ifdef NIMBLE_TWO
|
||||||
|
return NimBLEDevice::getClientByHandle(handle)->getRssi();
|
||||||
uint16_t connHandle = nimbleBluetoothConnHandle.load();
|
#else
|
||||||
|
return NimBLEDevice::getClientByID(handle)->getRssi();
|
||||||
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
|
|
||||||
const auto peers = bleServer->getPeerDevices();
|
|
||||||
if (!peers.empty()) {
|
|
||||||
connHandle = peers.front();
|
|
||||||
nimbleBluetoothConnHandle = connHandle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connHandle == BLE_HS_CONN_HANDLE_NONE) {
|
|
||||||
return 0; // Connection handle not available yet
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t rssi = 0;
|
|
||||||
const int rc = ble_gap_conn_rssi(connHandle, &rssi);
|
|
||||||
|
|
||||||
if (rc == 0) {
|
|
||||||
return rssi;
|
|
||||||
}
|
|
||||||
LOG_DEBUG("BLE RSSI read failed, rc=%d", rc);
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
return 0;
|
return 0; // FIXME figure out where to source this
|
||||||
}
|
}
|
||||||
|
|
||||||
void NimbleBluetooth::setup()
|
void NimbleBluetooth::setup()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user