Compare commits
48
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5f1fddd36 | ||
|
|
ee441dd7b2 | ||
|
|
178ae0a7f1 | ||
|
|
a08872299e | ||
|
|
cc9d433db0 | ||
|
|
60303968bb | ||
|
|
d3c7f05baa | ||
|
|
1971e5ab13 | ||
|
|
e2fda6598c | ||
|
|
982440d21d | ||
|
|
f9fea562aa | ||
|
|
c366296ab4 | ||
|
|
a076d97eb6 | ||
|
|
32dcd90abf | ||
|
|
5b7a5b2c22 | ||
|
|
5bce26d9b7 | ||
|
|
ef734b73c7 | ||
|
|
c7748a1602 | ||
|
|
91f930d5c0 | ||
|
|
f2c5cb0a05 | ||
|
|
e10e13226d | ||
|
|
5e69bc6c3f | ||
|
|
f3cb2bff78 | ||
|
|
894c5556cf | ||
|
|
2f92eb8499 | ||
|
|
8d08077412 | ||
|
|
00ec69201d | ||
|
|
4304480ca3 | ||
|
|
82aefd1af1 | ||
|
|
0f9eb86830 | ||
|
|
e2aa44ec54 | ||
|
|
1747e2d8e5 | ||
|
|
622aa046f1 | ||
|
|
0832330327 | ||
|
|
98e0604edf | ||
|
|
23ead5f2f1 | ||
|
|
af3739fd63 | ||
|
|
0148a89ddb | ||
|
|
6199faacf1 | ||
|
|
3261c04afb | ||
|
|
a541957480 | ||
|
|
767a748188 | ||
|
|
fc5556b8e6 | ||
|
|
4a1ff18f57 | ||
|
|
502c5af524 | ||
|
|
05707079bd | ||
|
|
1c05633fcd | ||
|
|
fce419b335 |
@@ -333,6 +333,7 @@ jobs:
|
||||
prerelease: true
|
||||
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
|
||||
tag_name: v${{ needs.version.outputs.long }}
|
||||
target_commitish: ${{ github.sha }}
|
||||
body: ${{ steps.release_notes.outputs.notes }}
|
||||
|
||||
- name: Download source deb
|
||||
|
||||
@@ -47,6 +47,10 @@ data/boot/logo.*
|
||||
managed_components/*
|
||||
arduino-lib-builder*
|
||||
dependencies.lock
|
||||
|
||||
# JLink / RTT debug artifacts (nRF SoCs)
|
||||
flash.jlink
|
||||
rtt_*.txt
|
||||
idf_component.yml
|
||||
CMakeLists.txt
|
||||
/sdkconfig.*
|
||||
|
||||
+4
-4
@@ -4,11 +4,11 @@ cli:
|
||||
plugins:
|
||||
sources:
|
||||
- id: trunk
|
||||
ref: v1.9.0
|
||||
ref: v1.10.0
|
||||
uri: https://github.com/trunk-io/plugins
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.528
|
||||
- checkov@3.2.529
|
||||
- renovate@43.150.0
|
||||
- prettier@3.8.3
|
||||
- trufflehog@3.95.3
|
||||
@@ -16,7 +16,7 @@ lint:
|
||||
- bandit@1.9.4
|
||||
- trivy@0.70.0
|
||||
- taplo@0.10.0
|
||||
- ruff@0.15.12
|
||||
- ruff@0.15.13
|
||||
- isort@8.0.1
|
||||
- markdownlint@0.48.0
|
||||
- oxipng@10.1.1
|
||||
@@ -26,7 +26,7 @@ lint:
|
||||
- hadolint@2.14.0
|
||||
- shfmt@3.6.0
|
||||
- shellcheck@0.11.0
|
||||
- black@26.3.1
|
||||
- black@26.5.1
|
||||
- git-diff-check
|
||||
- gitleaks@8.30.1
|
||||
- clang-format@16.0.3
|
||||
|
||||
+1
-1
@@ -38,4 +38,4 @@ cp bin/device-install.* $OUTDIR/
|
||||
cp bin/device-update.* $OUTDIR/
|
||||
|
||||
echo "Copying manifest"
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json || true
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||
|
||||
@@ -293,9 +293,12 @@ if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
|
||||
board_arch = infer_architecture(env.BoardConfig())
|
||||
should_skip_manifest = board_arch is None
|
||||
|
||||
# For host/native envs, avoid depending on 'buildprog' (some targets don't define it)
|
||||
mtjson_deps = [] if should_skip_manifest else ["buildprog"]
|
||||
if not should_skip_manifest and platform.name == "espressif32":
|
||||
# Most platforms can generate the manifest as part of the default 'buildprog' target.
|
||||
# Typically this passes success/failure properly.
|
||||
mtjson_deps = ["buildprog"]
|
||||
if platform.name == "espressif32":
|
||||
# On ESP32, we need to explicitly depend upon the binary to prevent fake-success upon failure.
|
||||
mtjson_deps = ["$BUILD_DIR/${PROGNAME}.bin"]
|
||||
# Build littlefs image as part of mtjson target
|
||||
# Equivalent to `pio run -t buildfs`
|
||||
target_lfs = env.DataToBin(
|
||||
@@ -309,7 +312,8 @@ if should_skip_manifest:
|
||||
|
||||
env.AddCustomTarget(
|
||||
name="mtjson",
|
||||
dependencies=mtjson_deps,
|
||||
# For host/native envs, avoid depending on 'buildprog' (some targets don't define it)
|
||||
dependencies=[],
|
||||
actions=[skip_manifest],
|
||||
title="Meshtastic Manifest (skipped)",
|
||||
description="mtjson generation is skipped for native environments",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"extra_flags": [
|
||||
"-D CDEBYTE_EORA_S3",
|
||||
"-D ARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-D ARDUINO_USB_MODE=0",
|
||||
"-D ARDUINO_USB_MODE=1",
|
||||
"-D ARDUINO_RUNNING_CORE=1",
|
||||
"-D ARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-D BOARD_HAS_PSRAM"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-DBOARD_HAS_PSRAM"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"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"],
|
||||
["0x2886", "0x1667"]
|
||||
],
|
||||
"usb_product": "HT-n5262",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "heltec_mesh_node_t1",
|
||||
"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": "Heltec Mesh Node T1",
|
||||
"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://heltec.org",
|
||||
"vendor": "Heltec"
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DHELTEC_WIRELESS_TRACKER",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"build": {
|
||||
"cpu": "cortex-m33",
|
||||
"f_cpu": "128000000L",
|
||||
"mcu": "nrf54l15",
|
||||
"zephyr": {
|
||||
"variant": "nrf54l15dk/nrf54l15/cpuapp"
|
||||
}
|
||||
},
|
||||
"connectivity": ["bluetooth"],
|
||||
"debug": {
|
||||
"default_tools": ["jlink"],
|
||||
"jlink_device": "nRF54L15_M33",
|
||||
"svd_path": "nrf54l15.svd"
|
||||
},
|
||||
"frameworks": ["zephyr"],
|
||||
"name": "Nordic nRF54L15-DK (PCA10156)",
|
||||
"upload": {
|
||||
"maximum_ram_size": 262144,
|
||||
"maximum_size": 1572864,
|
||||
"protocol": "jlink",
|
||||
"protocols": ["jlink"]
|
||||
},
|
||||
"url": "https://www.nordicsemi.com/Products/nRF54L15",
|
||||
"vendor": "Nordic Semiconductor"
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=0"
|
||||
],
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DLILYGO_TBEAM_1W",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DLILYGO_TBEAM_S3_CORE",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"extra_flags": [
|
||||
"-DLILYGO_T3S3_V1",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1",
|
||||
"-DBOARD_HAS_PSRAM"
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DUNPHONE_SPIN=9",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x640000,
|
||||
app1, app, ota_1, 0x650000,0x640000,
|
||||
spiffs, data, spiffs, 0xc90000,0x360000,
|
||||
coredump, data, coredump,0xFF0000,0x10000,
|
||||
|
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x330000,
|
||||
app1, app, ota_1, 0x340000,0x330000,
|
||||
spiffs, data, spiffs, 0x670000,0x180000,
|
||||
coredump, data, coredump,0x7F0000,0x10000,
|
||||
|
@@ -70,17 +70,6 @@ def esp32_create_combined_bin(source, target, env):
|
||||
|
||||
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"])
|
||||
# Enable Newlib Nano formatting to save space
|
||||
# ...but allow printf float support (compromise)
|
||||
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
# trunk-ignore-all(ruff/F821)
|
||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||
|
||||
# force linker response file instead of command line arguments
|
||||
|
||||
Import("env")
|
||||
|
||||
|
||||
def wrap_with_tempfile(command_key):
|
||||
command = env.get(command_key)
|
||||
if not command or not isinstance(command, str):
|
||||
return
|
||||
if "TEMPFILE(" in command:
|
||||
return
|
||||
env.Replace(**{command_key: "${TEMPFILE('%s')}" % command})
|
||||
|
||||
|
||||
# Force SCons to spill long commands into response files on this target.
|
||||
env.Replace(MAXLINELENGTH=8192)
|
||||
|
||||
for key in ("LINKCOM", "CXXLINKCOM", "SHLINKCOM", "SHCXXLINKCOM"):
|
||||
wrap_with_tempfile(key)
|
||||
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
# trunk-ignore-all(ruff/F821)
|
||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||
#
|
||||
# post:extra_scripts/nrf54l15_linker.py
|
||||
#
|
||||
# Fix for Zephyr two-pass link on nRF54L15:
|
||||
# platformio-build.py registers env.Depends("$PROG_PATH", final_ld_script) but
|
||||
# the SCons dependency chain is broken (final_ld_script Command never runs).
|
||||
# This script adds a PreAction on the final firmware binary that runs the gcc
|
||||
# preprocessing command directly (extracted from build.ninja) to generate
|
||||
# zephyr/linker.cmd before the link step.
|
||||
#
|
||||
# PlatformIO bundles an old Ninja that can't handle multi-output depslog rules,
|
||||
# so we parse the COMMAND line from build.ninja and run just the gcc -E part,
|
||||
# skipping the cmake_transform_depfile step (only needed for Ninja deps tracking).
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
Import("env")
|
||||
|
||||
if env.get("PIOENV") != "nrf54l15dk":
|
||||
pass # Only for the nrf54l15dk environment
|
||||
else:
|
||||
|
||||
def _extract_gcc_command(ninja_build):
|
||||
"""Parse build.ninja to find the gcc -E command that generates linker.cmd.
|
||||
|
||||
The rule format depends on the host:
|
||||
Windows (CMake's RunCMake wraps every command):
|
||||
COMMAND = cmd.exe /C "cd /D DIR && arm-none-eabi-gcc.exe ... -o linker.cmd && cmake.exe -E cmake_transform_depfile ..."
|
||||
POSIX (Linux/macOS — no wrapper):
|
||||
COMMAND = cd DIR && arm-none-eabi-gcc ... -o linker.cmd && cmake -E cmake_transform_depfile ...
|
||||
|
||||
Returns (gcc_cmd_string, cwd_path) or raises RuntimeError.
|
||||
"""
|
||||
in_rule = False
|
||||
with open(ninja_build, "r", encoding="utf-8", errors="replace") as f:
|
||||
for line in f:
|
||||
# Detect start of the linker.cmd custom command rule
|
||||
if not in_rule:
|
||||
if "build zephyr/linker.cmd" in line and "CUSTOM_COMMAND" in line:
|
||||
in_rule = True
|
||||
continue
|
||||
|
||||
stripped = line.strip()
|
||||
if not stripped.startswith("COMMAND = "):
|
||||
continue
|
||||
|
||||
command_val = stripped[len("COMMAND = ") :]
|
||||
|
||||
# On Windows the value is wrapped in `cmd.exe /C "..."` — strip
|
||||
# the wrapper. On POSIX hosts the inner sequence is the value
|
||||
# itself (no quoting layer).
|
||||
m = re.search(r'/C\s+"(.*)"\s*$', command_val)
|
||||
inner = m.group(1) if m else command_val
|
||||
parts = inner.split(" && ")
|
||||
|
||||
cwd = None
|
||||
gcc_cmd = None
|
||||
for part in parts:
|
||||
part = part.strip()
|
||||
if part.startswith("cd /D "): # Windows form
|
||||
cwd = part[len("cd /D ") :]
|
||||
elif part.startswith("cd "): # POSIX form
|
||||
cwd = part[len("cd ") :]
|
||||
elif "arm-none-eabi-gcc" in part:
|
||||
gcc_cmd = part
|
||||
|
||||
if not gcc_cmd:
|
||||
raise RuntimeError(
|
||||
"nRF54L15 linker fix: arm-none-eabi-gcc command not found in:\n%s"
|
||||
% inner[:400]
|
||||
)
|
||||
|
||||
return gcc_cmd, cwd
|
||||
|
||||
raise RuntimeError(
|
||||
"nRF54L15 linker fix: 'build zephyr/linker.cmd' rule not found in build.ninja"
|
||||
)
|
||||
|
||||
def _generate_linker_cmd(target, source, env):
|
||||
"""Generate zephyr/linker.cmd via direct gcc invocation before the final link."""
|
||||
build_dir = env.subst("$BUILD_DIR")
|
||||
zephyr_dir = os.path.join(build_dir, "zephyr")
|
||||
linker_cmd = os.path.join(zephyr_dir, "linker.cmd")
|
||||
|
||||
if os.path.exists(linker_cmd):
|
||||
return # Already present — nothing to do
|
||||
|
||||
ninja_build = os.path.join(build_dir, "build.ninja")
|
||||
if not os.path.exists(ninja_build):
|
||||
raise RuntimeError(
|
||||
"nRF54L15 linker fix: build.ninja not found at %s\n"
|
||||
"Run a full build first so CMake generates the Ninja files."
|
||||
% ninja_build
|
||||
)
|
||||
|
||||
gcc_cmd, cwd = _extract_gcc_command(ninja_build)
|
||||
run_cwd = cwd if cwd else zephyr_dir
|
||||
|
||||
print(
|
||||
"==> nRF54L15: Generating zephyr/linker.cmd (LINKER_ZEPHYR_FINAL) via GCC"
|
||||
)
|
||||
# gcc_cmd comes verbatim from our own build.ninja (never user input) and
|
||||
# contains Windows-style paths with spaces that cannot be safely argv-split
|
||||
# with shlex, so we run it via the platform shell. nosec/nosemgrep below
|
||||
# acknowledge this deliberate, scoped use of shell=True.
|
||||
result = subprocess.run( # nosec B602
|
||||
gcc_cmd,
|
||||
shell=True, # nosemgrep: python.lang.security.audit.subprocess-shell-true.subprocess-shell-true
|
||||
cwd=run_cwd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print("GCC stdout:", result.stdout[:2000])
|
||||
print("GCC stderr:", result.stderr[:2000])
|
||||
raise RuntimeError(
|
||||
"nRF54L15 linker fix: GCC failed to generate linker.cmd (rc=%d)"
|
||||
% result.returncode
|
||||
)
|
||||
if not os.path.exists(linker_cmd):
|
||||
raise RuntimeError(
|
||||
"nRF54L15 linker fix: GCC returned 0 but linker.cmd was not created at %s"
|
||||
% linker_cmd
|
||||
)
|
||||
print("==> linker.cmd generated successfully")
|
||||
|
||||
# Use PIOMAINPROG (set by ZephyrBuildProgram) to get the exact SCons node
|
||||
prog = env.get("PIOMAINPROG")
|
||||
if prog:
|
||||
env.AddPreAction(prog, _generate_linker_cmd)
|
||||
else:
|
||||
print(
|
||||
"[nrf54l15_linker] WARNING: PIOMAINPROG not set, falling back to $PROG_PATH"
|
||||
)
|
||||
env.AddPreAction(env.subst("$PROG_PATH"), _generate_linker_cmd)
|
||||
+6
-1
@@ -2,7 +2,7 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = tbeam
|
||||
default_envs = heltec-v3
|
||||
|
||||
extra_configs =
|
||||
variants/*/*.ini
|
||||
@@ -17,6 +17,7 @@ test_build_src = true
|
||||
extra_scripts =
|
||||
pre:bin/platformio-pre.py
|
||||
bin/platformio-custom.py
|
||||
post:extra_scripts/nrf54l15_linker.py
|
||||
; note: we add src to our include search path so that lmic_project_config can override
|
||||
; note: TINYGPS_OPTION_NO_CUSTOM_FIELDS is VERY important. We don't use custom fields and somewhere in that pile
|
||||
; of code is a heap corruption bug!
|
||||
@@ -185,12 +186,16 @@ lib_deps =
|
||||
https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library/archive/refs/tags/v1.1.2.zip
|
||||
# renovate: datasource=github-tags depName=SparkFun 9DoF IMU Breakout ICM 20948 packageName=sparkfun/SparkFun_ICM-20948_ArduinoLibrary
|
||||
https://github.com/sparkfun/SparkFun_ICM-20948_ArduinoLibrary/archive/refs/tags/v1.3.2.zip
|
||||
# renovate: datasource=github-tags depName=TDK InvenSense ICM42670P packageName=tdk-invn-oss/motion.arduino.ICM42670P
|
||||
https://github.com/tdk-invn-oss/motion.arduino.ICM42670P/archive/refs/tags/1.0.8.zip
|
||||
# renovate: datasource=github-tags depName=Adafruit LTR390 Library packageName=adafruit/Adafruit_LTR390
|
||||
https://github.com/adafruit/Adafruit_LTR390/archive/refs/tags/1.1.2.zip
|
||||
# renovate: datasource=github-tags depName=Adafruit PCT2075 packageName=adafruit/Adafruit_PCT2075
|
||||
https://github.com/adafruit/Adafruit_PCT2075/archive/refs/tags/1.0.6.zip
|
||||
# renovate: datasource=github-tags depName=DFRobot_BMM150 packageName=dfrobot/DFRobot_BMM150
|
||||
https://github.com/DFRobot/DFRobot_BMM150/archive/refs/tags/V1.0.0.zip
|
||||
# renovate: datasource=github-tags depName=SparkFun MMC5983MA Magnetometer packageName=sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library
|
||||
https://github.com/sparkfun/SparkFun_MMC5983MA_Magnetometer_Arduino_Library/archive/refs/tags/v1.1.4.zip
|
||||
# renovate: datasource=github-tags depName=Adafruit_TSL2561 packageName=adafruit/Adafruit_TSL2561
|
||||
https://github.com/adafruit/Adafruit_TSL2561/archive/refs/tags/1.1.3.zip
|
||||
# renovate: datasource=github-tags depName=BH1750_WE packageName=wollewald/BH1750_WE
|
||||
|
||||
+1
-1
Submodule protobufs updated: 7ffb4bb60d...519a0c7c9c
@@ -152,15 +152,16 @@ extern "C" void logLegacy(const char *level, const char *fmt, ...);
|
||||
// Default Bluetooth PIN
|
||||
#define defaultBLEPin 123456
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_CH390D)
|
||||
#if HAS_ETHERNET && defined(USE_ARDUINO_ETHERNET)
|
||||
#include <Ethernet.h> // arduino-libraries/Ethernet — supports W5500 auto-detect
|
||||
#elif HAS_ETHERNET && defined(USE_CH390D)
|
||||
#include <ESP32_CH390.h>
|
||||
#elif HAS_ETHERNET && !defined(USE_WS5500)
|
||||
#include <RAK13800_W5100S.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#if HAS_WIFI
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "DisplayFormatters.h"
|
||||
#include "MeshRadio.h"
|
||||
|
||||
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
|
||||
bool usePreset)
|
||||
@@ -11,33 +12,45 @@ const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaC
|
||||
}
|
||||
|
||||
switch (preset) {
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
|
||||
case PRESET(SHORT_TURBO):
|
||||
return useShortName ? "ShortT" : "ShortTurbo";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
|
||||
case PRESET(SHORT_SLOW):
|
||||
return useShortName ? "ShortS" : "ShortSlow";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
|
||||
case PRESET(SHORT_FAST):
|
||||
return useShortName ? "ShortF" : "ShortFast";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
|
||||
case PRESET(MEDIUM_SLOW):
|
||||
return useShortName ? "MedS" : "MediumSlow";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
|
||||
case PRESET(MEDIUM_FAST):
|
||||
return useShortName ? "MedF" : "MediumFast";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
|
||||
case PRESET(LONG_SLOW):
|
||||
return useShortName ? "LongS" : "LongSlow";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
|
||||
case PRESET(LONG_FAST):
|
||||
return useShortName ? "LongF" : "LongFast";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO:
|
||||
case PRESET(LONG_TURBO):
|
||||
return useShortName ? "LongT" : "LongTurbo";
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
|
||||
case PRESET(LONG_MODERATE):
|
||||
return useShortName ? "LongM" : "LongMod";
|
||||
break;
|
||||
case PRESET(LITE_FAST):
|
||||
return useShortName ? "LiteF" : "LiteFast";
|
||||
break;
|
||||
case PRESET(LITE_SLOW):
|
||||
return useShortName ? "LiteS" : "LiteSlow";
|
||||
break;
|
||||
case PRESET(NARROW_FAST):
|
||||
return useShortName ? "NarF" : "NarrowFast";
|
||||
break;
|
||||
case PRESET(NARROW_SLOW):
|
||||
return useShortName ? "NarS" : "NarrowSlow";
|
||||
break;
|
||||
default:
|
||||
return useShortName ? "Custom" : "Invalid";
|
||||
break;
|
||||
|
||||
+1
-1
@@ -277,7 +277,7 @@ void fsInit()
|
||||
*/
|
||||
void setupSDCard()
|
||||
{
|
||||
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI)
|
||||
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI) && !defined(HAS_SD_MMC)
|
||||
concurrency::LockGuard g(spiLock);
|
||||
SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
|
||||
if (!SD.begin(SDCARD_CS, SDHandler, SD_SPI_FREQUENCY)) {
|
||||
|
||||
@@ -48,6 +48,14 @@ using namespace STM32_LittleFS_Namespace;
|
||||
using namespace Adafruit_LittleFS_Namespace;
|
||||
#endif
|
||||
|
||||
#if defined(ARCH_NRF54L15)
|
||||
// nRF54L15 — Zephyr LittleFS on 36 KB storage_partition (internal RRAM)
|
||||
#include "InternalFileSystem.h"
|
||||
#define FSCom InternalFS
|
||||
#define FSBegin() FSCom.begin()
|
||||
using namespace Adafruit_LittleFS_Namespace;
|
||||
#endif
|
||||
|
||||
void fsInit();
|
||||
void fsListFiles();
|
||||
bool copyFile(const char *from, const char *to);
|
||||
|
||||
+126
-83
@@ -24,6 +24,13 @@
|
||||
#include "meshUtils.h"
|
||||
#include "power/PowerHAL.h"
|
||||
#include "sleep.h"
|
||||
#ifdef ARCH_ESP32
|
||||
// #include <driver/adc.h>
|
||||
#include <esp_adc/adc_cali.h>
|
||||
#include <esp_adc/adc_cali_scheme.h>
|
||||
#include <esp_adc/adc_oneshot.h>
|
||||
#include <esp_err.h>
|
||||
#endif
|
||||
|
||||
#if defined(ARCH_PORTDUINO)
|
||||
#include "api/WiFiServerAPI.h"
|
||||
@@ -63,9 +70,8 @@
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#endif
|
||||
@@ -77,21 +83,86 @@
|
||||
#if defined(BATTERY_PIN) && defined(ARCH_ESP32)
|
||||
|
||||
#ifndef BAT_MEASURE_ADC_UNIT // ADC1 is default
|
||||
static const adc1_channel_t adc_channel = ADC_CHANNEL;
|
||||
static const adc_channel_t adc_channel = ADC_CHANNEL;
|
||||
static const adc_unit_t unit = ADC_UNIT_1;
|
||||
#else // ADC2
|
||||
static const adc2_channel_t adc_channel = ADC_CHANNEL;
|
||||
#else // ADC2
|
||||
static const adc_channel_t adc_channel = ADC_CHANNEL;
|
||||
static const adc_unit_t unit = ADC_UNIT_2;
|
||||
RTC_NOINIT_ATTR uint64_t RTC_reg_b;
|
||||
|
||||
#endif // BAT_MEASURE_ADC_UNIT
|
||||
|
||||
esp_adc_cal_characteristics_t *adc_characs = (esp_adc_cal_characteristics_t *)calloc(1, sizeof(esp_adc_cal_characteristics_t));
|
||||
static adc_oneshot_unit_handle_t adc_handle = nullptr;
|
||||
static adc_cali_handle_t adc_cali_handle = nullptr;
|
||||
static bool adc_calibrated = false;
|
||||
#ifndef ADC_ATTENUATION
|
||||
static const adc_atten_t atten = ADC_ATTEN_DB_12;
|
||||
#else
|
||||
static const adc_atten_t atten = ADC_ATTENUATION;
|
||||
#endif
|
||||
#ifdef ADC_BITWIDTH
|
||||
static const adc_bitwidth_t adc_width = ADC_BITWIDTH;
|
||||
#else
|
||||
static const adc_bitwidth_t adc_width = ADC_BITWIDTH_DEFAULT;
|
||||
#endif
|
||||
|
||||
static int adcBitWidthToBits(adc_bitwidth_t width)
|
||||
{
|
||||
switch (width) {
|
||||
case ADC_BITWIDTH_9:
|
||||
return 9;
|
||||
case ADC_BITWIDTH_10:
|
||||
return 10;
|
||||
case ADC_BITWIDTH_11:
|
||||
return 11;
|
||||
case ADC_BITWIDTH_12:
|
||||
return 12;
|
||||
#ifdef ADC_BITWIDTH_13
|
||||
case ADC_BITWIDTH_13:
|
||||
return 13;
|
||||
#endif
|
||||
default:
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
|
||||
static bool initAdcCalibration()
|
||||
{
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = adc_width,
|
||||
};
|
||||
esp_err_t ret = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle);
|
||||
if (ret == ESP_OK) {
|
||||
LOG_INFO("ADC calibration: curve fitting enabled");
|
||||
return true;
|
||||
}
|
||||
if (ret != ESP_ERR_NOT_SUPPORTED) {
|
||||
LOG_WARN("ADC calibration: curve fitting failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = adc_width,
|
||||
.default_vref = DEFAULT_VREF,
|
||||
};
|
||||
esp_err_t ret = adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle);
|
||||
if (ret == ESP_OK) {
|
||||
LOG_INFO("ADC calibration: line fitting enabled");
|
||||
return true;
|
||||
}
|
||||
if (ret != ESP_ERR_NOT_SUPPORTED) {
|
||||
LOG_WARN("ADC calibration: line fitting failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
#endif
|
||||
|
||||
LOG_INFO("ADC calibration not supported; using approximate scaling");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // BATTERY_PIN && ARCH_ESP32
|
||||
|
||||
#ifdef EXT_PWR_DETECT
|
||||
@@ -367,8 +438,20 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
scaled *= operativeAdcMultiplier;
|
||||
#elif defined(ARCH_ESP32) // ADC block for espressif platforms
|
||||
raw = espAdcRead();
|
||||
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
|
||||
scaled *= operativeAdcMultiplier;
|
||||
int voltage_mv = 0;
|
||||
if (adc_calibrated && adc_cali_handle) {
|
||||
if (adc_cali_raw_to_voltage(adc_cali_handle, raw, &voltage_mv) != ESP_OK) {
|
||||
LOG_WARN("ADC calibration read failed; using raw value");
|
||||
voltage_mv = 0;
|
||||
}
|
||||
}
|
||||
if (voltage_mv == 0) {
|
||||
// Fallback approximate conversion without calibration
|
||||
const int bits = adcBitWidthToBits(adc_width);
|
||||
const float max_code = powf(2.0f, bits) - 1.0f;
|
||||
voltage_mv = (int)((raw / max_code) * DEFAULT_VREF);
|
||||
}
|
||||
scaled = voltage_mv * operativeAdcMultiplier;
|
||||
#else // block for all other platforms
|
||||
#ifdef ARCH_NRF52
|
||||
concurrency::LockGuard saadcGuard(concurrency::nrf52SaadcLock);
|
||||
@@ -410,51 +493,22 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
uint32_t raw = 0;
|
||||
uint8_t raw_c = 0; // raw reading counter
|
||||
|
||||
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
||||
if (!adc_handle) {
|
||||
LOG_ERROR("ADC oneshot handle not initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
int val_ = adc1_get_raw(adc_channel);
|
||||
if (val_ >= 0) { // save only valid readings
|
||||
raw += val_;
|
||||
int val = 0;
|
||||
esp_err_t err = adc_oneshot_read(adc_handle, adc_channel, &val);
|
||||
if (err == ESP_OK) {
|
||||
raw += val;
|
||||
raw_c++;
|
||||
}
|
||||
// delayMicroseconds(100);
|
||||
}
|
||||
#else // ADC2
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3 // ESP32S3
|
||||
// ADC2 wifi bug workaround not required, breaks compile
|
||||
// On ESP32S3, ADC2 can take turns with Wifi (?)
|
||||
|
||||
int32_t adc_buf;
|
||||
esp_err_t read_result;
|
||||
|
||||
// Multiple samples
|
||||
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
adc_buf = 0;
|
||||
read_result = -1;
|
||||
|
||||
read_result = adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf);
|
||||
if (read_result == ESP_OK) {
|
||||
raw += adc_buf;
|
||||
raw_c++; // Count valid samples
|
||||
} else {
|
||||
LOG_DEBUG("An attempt to sample ADC2 failed");
|
||||
LOG_DEBUG("ADC read failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
#else // Other ESP32
|
||||
int32_t adc_buf = 0;
|
||||
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
// ADC2 wifi bug workaround, see
|
||||
// https://github.com/espressif/arduino-esp32/issues/102
|
||||
WRITE_PERI_REG(SENS_SAR_READ_CTRL2_REG, RTC_reg_b);
|
||||
SET_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DATA_INV);
|
||||
adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf);
|
||||
raw += adc_buf;
|
||||
raw_c++;
|
||||
}
|
||||
#endif // BAT_MEASURE_ADC_UNIT
|
||||
|
||||
#endif // End BAT_MEASURE_ADC_UNIT
|
||||
return (raw / (raw_c < 1 ? 1 : raw_c));
|
||||
}
|
||||
#endif
|
||||
@@ -666,42 +720,31 @@ bool Power::analogInit()
|
||||
#ifdef ARCH_STM32WL
|
||||
analogReadResolution(BATTERY_SENSE_RESOLUTION_BITS);
|
||||
#elif defined(ARCH_ESP32) // ESP32 needs special analog stuff
|
||||
adc_oneshot_unit_init_cfg_t init_config = {
|
||||
.unit_id = unit,
|
||||
};
|
||||
|
||||
#ifndef ADC_WIDTH // max resolution by default
|
||||
static const adc_bits_width_t width = ADC_WIDTH_BIT_12;
|
||||
#else
|
||||
static const adc_bits_width_t width = ADC_WIDTH;
|
||||
#endif
|
||||
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
|
||||
adc1_config_width(width);
|
||||
adc1_config_channel_atten(adc_channel, atten);
|
||||
#else // ADC2
|
||||
adc2_config_channel_atten(adc_channel, atten);
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32S3
|
||||
// ADC2 wifi bug workaround
|
||||
// Not required with ESP32S3, breaks compile
|
||||
RTC_reg_b = READ_PERI_REG(SENS_SAR_READ_CTRL2_REG);
|
||||
#endif
|
||||
#endif
|
||||
// calibrate ADC
|
||||
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_characs);
|
||||
// show ADC characterization base
|
||||
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
|
||||
LOG_INFO("ADC config based on Two Point values stored in eFuse");
|
||||
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
|
||||
LOG_INFO("ADC config based on reference voltage stored in eFuse");
|
||||
if (!adc_handle) {
|
||||
esp_err_t err = adc_oneshot_new_unit(&init_config, &adc_handle);
|
||||
if (err != ESP_OK) {
|
||||
LOG_ERROR("ADC oneshot init failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S3
|
||||
// ESP32S3
|
||||
else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP_FIT) {
|
||||
LOG_INFO("ADC config based on Two Point values and fitting curve "
|
||||
"coefficients stored in eFuse");
|
||||
|
||||
adc_oneshot_chan_cfg_t chan_cfg = {
|
||||
.atten = atten,
|
||||
.bitwidth = adc_width,
|
||||
};
|
||||
|
||||
esp_err_t err = adc_oneshot_config_channel(adc_handle, adc_channel, &chan_cfg);
|
||||
if (err != ESP_OK) {
|
||||
LOG_ERROR("ADC channel config failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
LOG_INFO("ADC config based on default reference voltage");
|
||||
}
|
||||
#endif // ARCH_ESP32
|
||||
|
||||
adc_calibrated = initAdcCalibration();
|
||||
#endif // ARCH_ESP32
|
||||
|
||||
// NRF52 ADC init moved to powerHAL_init in nrf52 platform
|
||||
|
||||
|
||||
@@ -225,6 +225,8 @@ void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_
|
||||
isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected();
|
||||
#elif defined(ARCH_NRF52)
|
||||
isBleConnected = nrf52Bluetooth != nullptr && nrf52Bluetooth->isConnected();
|
||||
#elif defined(ARCH_NRF54L15)
|
||||
isBleConnected = nrf54l15Bluetooth != nullptr && nrf54l15Bluetooth->isConnected();
|
||||
#endif
|
||||
if (isBleConnected) {
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
@@ -241,6 +243,8 @@ void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_
|
||||
nimbleBluetooth->sendLog(buffer.get(), size);
|
||||
#elif defined(ARCH_NRF52)
|
||||
nrf52Bluetooth->sendLog(buffer.get(), size);
|
||||
#elif defined(ARCH_NRF54L15)
|
||||
nrf54l15Bluetooth->sendLog(buffer.get(), size);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -133,11 +133,12 @@ bool AirTime::isTxAllowedChannelUtil(bool polite)
|
||||
|
||||
bool AirTime::isTxAllowedAirUtil()
|
||||
{
|
||||
if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100) {
|
||||
if (utilizationTXPercent() < myRegion->dutyCycle * polite_duty_cycle_percent / 100) {
|
||||
float effectiveDutyCycle = getEffectiveDutyCycle();
|
||||
if (!config.lora.override_duty_cycle && effectiveDutyCycle < 100) {
|
||||
if (utilizationTXPercent() < effectiveDutyCycle * polite_duty_cycle_percent / 100) {
|
||||
return true;
|
||||
} else {
|
||||
LOG_WARN("TX air util. >%f%%. Skip send", myRegion->dutyCycle * polite_duty_cycle_percent / 100);
|
||||
LOG_WARN("TX air util. >%f%%. Skip send", effectiveDutyCycle * polite_duty_cycle_percent / 100);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endif
|
||||
#if __has_include("SensorRtcHelper.hpp")
|
||||
#include "SensorRtcHelper.hpp"
|
||||
// SensorLib defines isBitSet as a macro; undefine it here to avoid conflicts
|
||||
// with the SparkFun MMC5983MA library, which has a class method of the same name.
|
||||
#ifdef isBitSet
|
||||
#undef isBitSet
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Offer chance for variant-specific defines */
|
||||
@@ -243,6 +248,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define QMI8658_ADDR 0x6B
|
||||
#define QMC5883L_ADDR 0x0D
|
||||
#define HMC5883L_ADDR 0x1E
|
||||
#define MMC5983MA_ADDR 0x30
|
||||
#define SHTC3_ADDR 0x70
|
||||
#define LPS22HB_ADDR 0x5C
|
||||
#define LPS22HB_ADDR_ALT 0x5D
|
||||
@@ -292,6 +298,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define DA217_ADDR 0x26
|
||||
#define BMI270_ADDR 0x68
|
||||
#define BMI270_ADDR_ALT 0x69
|
||||
#define ICM42607P_ADDR 0x68
|
||||
#define ICM42607P_ADDR_ALT 0x69
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// LED
|
||||
@@ -561,5 +569,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define HAS_SCREEN 0
|
||||
#endif
|
||||
|
||||
#ifndef USE_ETHERNET_DEFAULT
|
||||
#define USE_ETHERNET_DEFAULT 0
|
||||
#endif
|
||||
|
||||
#include "DebugConfiguration.h"
|
||||
#include "RF95Configuration.h"
|
||||
|
||||
@@ -37,8 +37,15 @@ ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
|
||||
|
||||
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
|
||||
{
|
||||
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948, QMA6100P, BMM150, BMI270};
|
||||
return firstOfOrNONE(10, types);
|
||||
ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX,
|
||||
ICM20948, QMA6100P, BMM150, BMI270, ICM42607P};
|
||||
return firstOfOrNONE(11, types);
|
||||
}
|
||||
|
||||
ScanI2C::FoundDevice ScanI2C::firstMagnetometer() const
|
||||
{
|
||||
ScanI2C::DeviceType types[] = {MMC5983MA};
|
||||
return firstOfOrNONE(1, types);
|
||||
}
|
||||
|
||||
ScanI2C::FoundDevice ScanI2C::firstAQI() const
|
||||
|
||||
@@ -41,6 +41,7 @@ class ScanI2C
|
||||
QMI8658,
|
||||
QMC5883L,
|
||||
HMC5883L,
|
||||
MMC5983MA,
|
||||
PMSA003I,
|
||||
QMA6100P,
|
||||
MPU6050,
|
||||
@@ -65,6 +66,7 @@ class ScanI2C
|
||||
FT6336U,
|
||||
STK8BAXX,
|
||||
ICM20948,
|
||||
ICM42607P,
|
||||
SCD4X,
|
||||
MAX30102,
|
||||
TPS65233,
|
||||
@@ -149,6 +151,8 @@ class ScanI2C
|
||||
|
||||
FoundDevice firstAccelerometer() const;
|
||||
|
||||
FoundDevice firstMagnetometer() const;
|
||||
|
||||
FoundDevice firstAQI() const;
|
||||
|
||||
FoundDevice firstRGBLED() const;
|
||||
|
||||
@@ -179,8 +179,22 @@ String readSEN5xProductName(TwoWire *i2cBus, uint8_t address)
|
||||
#endif
|
||||
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
static uint8_t crcSHT2X(const uint8_t *data, uint8_t len)
|
||||
{
|
||||
uint8_t crc = 0;
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
crc ^= data[i];
|
||||
for (uint8_t bit = 0; bit < 8; bit++) {
|
||||
crc = (crc & 0x80) ? (crc << 1) ^ 0x31 : crc << 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool detectSHT21SerialNumber(TwoWire *i2cBus, uint8_t address)
|
||||
{
|
||||
uint8_t serialA[8] = {0};
|
||||
uint8_t serialB[6] = {0};
|
||||
|
||||
i2cBus->beginTransmission(address);
|
||||
i2cBus->write(0xFA);
|
||||
@@ -189,12 +203,13 @@ bool detectSHT21SerialNumber(TwoWire *i2cBus, uint8_t address)
|
||||
if (i2cBus->endTransmission() != 0)
|
||||
return false;
|
||||
|
||||
if (i2cBus->requestFrom(address, (uint8_t)8) != 8)
|
||||
if (i2cBus->requestFrom(address, (uint8_t)sizeof(serialA)) != sizeof(serialA))
|
||||
return false;
|
||||
|
||||
// Just flush the data
|
||||
while (i2cBus->available() < 8) {
|
||||
i2cBus->read();
|
||||
for (uint8_t i = 0; i < sizeof(serialA); i++) {
|
||||
if (!i2cBus->available())
|
||||
return false;
|
||||
serialA[i] = i2cBus->read();
|
||||
}
|
||||
|
||||
i2cBus->beginTransmission(address);
|
||||
@@ -204,16 +219,18 @@ bool detectSHT21SerialNumber(TwoWire *i2cBus, uint8_t address)
|
||||
if (i2cBus->endTransmission() != 0)
|
||||
return false;
|
||||
|
||||
if (i2cBus->requestFrom(address, (uint8_t)6) != 6)
|
||||
if (i2cBus->requestFrom(address, (uint8_t)sizeof(serialB)) != sizeof(serialB))
|
||||
return false;
|
||||
|
||||
// Just flush the data
|
||||
while (i2cBus->available() < 6) {
|
||||
i2cBus->read();
|
||||
for (uint8_t i = 0; i < sizeof(serialB); i++) {
|
||||
if (!i2cBus->available())
|
||||
return false;
|
||||
serialB[i] = i2cBus->read();
|
||||
}
|
||||
|
||||
// Assume we detect the SHT21 if something came back from the request
|
||||
return true;
|
||||
return crcSHT2X(&serialA[0], 1) == serialA[1] && crcSHT2X(&serialA[2], 1) == serialA[3] &&
|
||||
crcSHT2X(&serialA[4], 1) == serialA[5] && crcSHT2X(&serialA[6], 1) == serialA[7] &&
|
||||
crcSHT2X(&serialB[0], 2) == serialB[2] && crcSHT2X(&serialB[3], 2) == serialB[5];
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -343,9 +360,6 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "ST7567", (uint8_t)addr.address);
|
||||
#ifdef HAS_NCP5623
|
||||
SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623", (uint8_t)addr.address);
|
||||
#endif
|
||||
#ifdef HAS_LP5562
|
||||
SCAN_SIMPLE_CASE(LP5562_ADDR, LP5562, "LP5562", (uint8_t)addr.address);
|
||||
#endif
|
||||
case XPOWERS_AXP192_AXP2101_ADDRESS:
|
||||
// Do we have the axp2101/192 or the TCA8418
|
||||
@@ -583,6 +597,18 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
#else
|
||||
SCAN_SIMPLE_CASE(PMSA003I_ADDR, PMSA003I, "PMSA003I", (uint8_t)addr.address)
|
||||
#endif
|
||||
case MMC5983MA_ADDR:
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x2F), 1);
|
||||
if (registerValue == 0x30) {
|
||||
type = MMC5983MA;
|
||||
logFoundDevice("MMC5983MA", (uint8_t)addr.address);
|
||||
#ifdef HAS_LP5562
|
||||
} else {
|
||||
type = LP5562;
|
||||
logFoundDevice("LP5562", (uint8_t)addr.address);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case BMA423_ADDR: // this can also be LIS3DH_ADDR_ALT
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 2);
|
||||
if (registerValue == 0x3300 || registerValue == 0x3333) { // RAK4631 WisBlock has LIS3DH register at 0x3333
|
||||
@@ -737,8 +763,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
}
|
||||
break;
|
||||
|
||||
case ICM20948_ADDR: // same as BMX160_ADDR, BMI270_ADDR_ALT, and SEN5X_ADDR
|
||||
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR, BMI270_ADDR
|
||||
case ICM20948_ADDR: // same as BMX160_ADDR, BMI270_ADDR_ALT, ICM42607P_ADDR_ALT, and SEN5X_ADDR
|
||||
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR, BMI270_ADDR, and ICM42607P_ADDR
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
|
||||
#ifdef HAS_ICM20948
|
||||
type = ICM20948;
|
||||
@@ -758,6 +784,12 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
logFoundDevice("BMX160", (uint8_t)addr.address);
|
||||
break;
|
||||
} else {
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x75), 1); // WHO_AM_I
|
||||
if (registerValue == 0x60) {
|
||||
type = ICM42607P;
|
||||
logFoundDevice("ICM-42607-P", (uint8_t)addr.address);
|
||||
break;
|
||||
}
|
||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_AIR_QUALITY_SENSOR
|
||||
String prod = "";
|
||||
prod = readSEN5xProductName(i2cBus, addr.address);
|
||||
|
||||
@@ -425,6 +425,11 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
||||
#elif defined(USE_SSD1306)
|
||||
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#if defined(OLED_Y_OFFSET_PAGES)
|
||||
// Panels whose active window does not start at GDDRAM row 0 (e.g. 72x40
|
||||
// modules on pages 3..7) need a fixed vertical page shift on every write.
|
||||
static_cast<SSD1306Wire *>(dispdev)->setYOffset(OLED_Y_OFFSET_PAGES);
|
||||
#endif
|
||||
#elif defined(USE_SPISSD1306)
|
||||
dispdev = new SSD1306Spi(SSD1306_RESET, SSD1306_RS, SSD1306_NSS, GEOMETRY_64_48);
|
||||
if (!dispdev->init()) {
|
||||
@@ -910,7 +915,7 @@ int32_t Screen::runOnce()
|
||||
|
||||
#ifndef DISABLE_WELCOME_UNSET
|
||||
if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
menuHandler::LoraRegionPicker();
|
||||
#else
|
||||
menuHandler::OnboardMessage();
|
||||
@@ -1147,7 +1152,7 @@ void Screen::setFrames(FrameFocus focus)
|
||||
#if defined(DISPLAY_CLOCK_FRAME)
|
||||
if (!hiddenFrames.clock) {
|
||||
fsi.positions.clock = numframes;
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
normalFrames[numframes++] = graphics::ClockRenderer::drawAnalogClockFrame;
|
||||
#else
|
||||
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
|
||||
@@ -1606,7 +1611,7 @@ void Screen::showFrame(FrameDirection direction)
|
||||
|
||||
void Screen::setFastFramerate()
|
||||
{
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
dispdev->clear();
|
||||
#if GRAPHICS_TFT_COLORING_ENABLED
|
||||
prepareFrameColorRegions();
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
|
||||
#define FONT_MEDIUM FONT_LARGE_LOCAL // Height: 28
|
||||
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
|
||||
#elif defined(M5STACK_UNITC6L)
|
||||
#elif defined(OLED_TINY)
|
||||
#define FONT_SMALL FONT_SMALL_LOCAL // Height: 13
|
||||
#define FONT_MEDIUM FONT_SMALL_LOCAL // Height: 13
|
||||
#define FONT_LARGE FONT_SMALL_LOCAL // Height: 13
|
||||
|
||||
@@ -31,6 +31,12 @@ ScreenResolution determineScreenResolution(int16_t screenheight, int16_t screenw
|
||||
return ScreenResolution::UltraLow;
|
||||
}
|
||||
|
||||
#ifdef DISPLAY_FORCE_SMALL_FONTS
|
||||
if (screenwidth <= 160 && screenheight <= 80) {
|
||||
return ScreenResolution::Low;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Standard OLED screens
|
||||
if (screenwidth > 128 && screenheight <= 64) {
|
||||
return ScreenResolution::Low;
|
||||
@@ -240,7 +246,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
||||
|
||||
int batteryX = 1;
|
||||
int batteryY = HEADER_OFFSET_Y + 1;
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
#if !defined(OLED_TINY)
|
||||
// === Battery Icons ===
|
||||
if (usbPowered && !isCharging) { // This is a basic check to determine USB Powered is flagged but not charging
|
||||
batteryX += 1;
|
||||
|
||||
@@ -1458,7 +1458,8 @@ void TFTDisplay::sendCommand(uint8_t com)
|
||||
digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON);
|
||||
#elif defined(HACKADAY_COMMUNICATOR)
|
||||
tft->displayOn();
|
||||
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096)
|
||||
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096) && \
|
||||
!defined(HELTEC_MESH_NODE_T1)
|
||||
tft->wakeup();
|
||||
tft->powerSaveOff();
|
||||
#endif
|
||||
@@ -1469,7 +1470,7 @@ void TFTDisplay::sendCommand(uint8_t com)
|
||||
#ifdef UNPHONE
|
||||
unphone.backlight(true); // using unPhone library
|
||||
#endif
|
||||
#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096)
|
||||
#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1)
|
||||
#elif !defined(M5STACK) && !defined(ST7789_CS) && \
|
||||
!defined(HACKADAY_COMMUNICATOR) // T-Deck gets brightness set in Screen.cpp in the handleSetOn function
|
||||
tft->setBrightness(172);
|
||||
@@ -1485,7 +1486,8 @@ void TFTDisplay::sendCommand(uint8_t com)
|
||||
digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON);
|
||||
#elif defined(HACKADAY_COMMUNICATOR)
|
||||
tft->displayOff();
|
||||
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096)
|
||||
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) && !defined(HELTEC_MESH_NODE_T096) && \
|
||||
!defined(HELTEC_MESH_NODE_T1)
|
||||
tft->sleep();
|
||||
tft->powerSaveOn();
|
||||
#endif
|
||||
@@ -1496,7 +1498,7 @@ void TFTDisplay::sendCommand(uint8_t com)
|
||||
#ifdef UNPHONE
|
||||
unphone.backlight(false); // using unPhone library
|
||||
#endif
|
||||
#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096)
|
||||
#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1)
|
||||
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
|
||||
tft->setBrightness(0);
|
||||
#endif
|
||||
@@ -1511,7 +1513,7 @@ void TFTDisplay::sendCommand(uint8_t com)
|
||||
|
||||
void TFTDisplay::setDisplayBrightness(uint8_t _brightness)
|
||||
{
|
||||
#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096)
|
||||
#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1)
|
||||
// todo
|
||||
#elif !defined(HACKADAY_COMMUNICATOR)
|
||||
tft->setBrightness(_brightness);
|
||||
@@ -1531,7 +1533,8 @@ bool TFTDisplay::hasTouch(void)
|
||||
{
|
||||
#ifdef RAK14014
|
||||
return true;
|
||||
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096)
|
||||
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096) && \
|
||||
!defined(HELTEC_MESH_NODE_T1)
|
||||
return tft->touch() != nullptr;
|
||||
#else
|
||||
return false;
|
||||
@@ -1550,7 +1553,8 @@ bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096)
|
||||
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR) && !defined(HELTEC_MESH_NODE_T096) && \
|
||||
!defined(HELTEC_MESH_NODE_T1)
|
||||
return tft->getTouch(x, y);
|
||||
#else
|
||||
return false;
|
||||
@@ -1567,7 +1571,7 @@ bool TFTDisplay::connect()
|
||||
{
|
||||
concurrency::LockGuard g(spiLock);
|
||||
LOG_INFO("Do TFT init");
|
||||
#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096)
|
||||
#if defined(RAK14014) || defined(HELTEC_MESH_NODE_T096) || defined(HELTEC_MESH_NODE_T1)
|
||||
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 */);
|
||||
|
||||
@@ -142,8 +142,9 @@ void VirtualKeyboard::draw(OLEDDisplay *display, int16_t offsetX, int16_t offset
|
||||
if (keyboardStartY < 0)
|
||||
keyboardStartY = 0;
|
||||
} else {
|
||||
// Default (non-wide, non-64px) behavior: use key height heuristic and place at bottom
|
||||
cellH = KEY_HEIGHT;
|
||||
// Default (non-wide, non-64px) e.g. SH1107 128x128:
|
||||
// cellH = FONT_HEIGHT_SMALL - 2 so rows are tighter while still hosting the font
|
||||
cellH = std::max((int)KEY_HEIGHT, FONT_HEIGHT_SMALL - 2);
|
||||
int keyboardHeight = KEYBOARD_ROWS * cellH;
|
||||
keyboardStartY = screenH - keyboardHeight;
|
||||
if (keyboardStartY < 0)
|
||||
@@ -446,11 +447,8 @@ void VirtualKeyboard::drawKey(OLEDDisplay *display, const VirtualKey &key, bool
|
||||
if (textX < x)
|
||||
textX = x; // guard
|
||||
} else {
|
||||
if (display->getHeight() <= 64 && (key.character >= '0' && key.character <= '9')) {
|
||||
textX = x + (width - textWidth + 1) / 2;
|
||||
} else {
|
||||
textX = x + (width - textWidth) / 2;
|
||||
}
|
||||
// Use ceil rounding for all screens (consistent with 128x64 behavior for numbers)
|
||||
textX = x + (width - textWidth + 1) / 2;
|
||||
}
|
||||
int contentTop = y;
|
||||
int contentH = height;
|
||||
@@ -746,4 +744,4 @@ bool VirtualKeyboard::isTimedOut() const
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -460,7 +460,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
||||
nameX = (SCREEN_WIDTH - textWidth) / 2;
|
||||
display->drawString(nameX, getTextPositions(display)[line++], frequencyslot);
|
||||
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
#if !defined(OLED_TINY)
|
||||
// === Fifth Row: Channel Utilization ===
|
||||
const char *chUtil = "ChUtil:";
|
||||
char chUtilPercentage[10];
|
||||
@@ -592,7 +592,7 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
|
||||
// Label
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->drawString(labelX, getTextPositions(display)[line], label);
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
#if !defined(OLED_TINY)
|
||||
// Bar
|
||||
int barY = getTextPositions(display)[line] + (FONT_HEIGHT_SMALL - barHeight) / 2;
|
||||
display->setColor(WHITE);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#if HAS_SCREEN
|
||||
#include "ClockRenderer.h"
|
||||
#include "Default.h"
|
||||
#include "DisplayFormatters.h"
|
||||
#include "GPS.h"
|
||||
#include "MenuHandler.h"
|
||||
#include "MeshRadio.h"
|
||||
@@ -180,6 +181,8 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
||||
{"US", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_US},
|
||||
{"EU_433", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_EU_433},
|
||||
{"EU_868", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_EU_868},
|
||||
{"EU_866", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_EU_866},
|
||||
{"EU_868_NARROW", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_EU_N_868},
|
||||
{"CN", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_CN},
|
||||
{"JP", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_JP},
|
||||
{"ANZ", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_ANZ},
|
||||
@@ -203,6 +206,7 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
||||
{"KZ_863", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_KZ_863},
|
||||
{"NP_865", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_NP_865},
|
||||
{"BR_902", OptionsAction::Select, meshtastic_Config_LoRaConfig_RegionCode_BR_902},
|
||||
|
||||
};
|
||||
|
||||
constexpr size_t regionCount = sizeof(regionOptions) / sizeof(regionOptions[0]);
|
||||
@@ -244,7 +248,7 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
|
||||
#endif
|
||||
config.lora.tx_enabled = true;
|
||||
initRegion();
|
||||
if (myRegion->dutyCycle < 100) {
|
||||
if (getEffectiveDutyCycle() < 100) {
|
||||
config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit
|
||||
}
|
||||
|
||||
@@ -378,42 +382,64 @@ void menuHandler::FrequencySlotPicker()
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
}
|
||||
|
||||
// Maximum presets any region can have + 1 for Back
|
||||
static constexpr int MAX_PRESET_OPTIONS = 16;
|
||||
|
||||
static BannerOverlayOptions buildRegionPresetBanner()
|
||||
{
|
||||
// Static storage reused each call — safe because the banner is shown immediately after.
|
||||
static const char *optionsArray[MAX_PRESET_OPTIONS];
|
||||
static int optionsEnumArray[MAX_PRESET_OPTIONS];
|
||||
static char presetLabelBuf[MAX_PRESET_OPTIONS][12]; // scratch space for name copies
|
||||
int count = 0;
|
||||
|
||||
optionsArray[count] = "Back";
|
||||
optionsEnumArray[count++] = -1;
|
||||
|
||||
if (myRegion && myRegion->profile) {
|
||||
const meshtastic_Config_LoRaConfig_ModemPreset *presets = myRegion->getAvailablePresets();
|
||||
size_t numPresets = myRegion->getNumPresets();
|
||||
for (size_t i = 0; i < numPresets && count < MAX_PRESET_OPTIONS; ++i) {
|
||||
const char *name = DisplayFormatters::getModemPresetDisplayName(presets[i], false, true);
|
||||
strncpy(presetLabelBuf[count], name, sizeof(presetLabelBuf[count]) - 1);
|
||||
presetLabelBuf[count][sizeof(presetLabelBuf[count]) - 1] = '\0';
|
||||
optionsArray[count] = presetLabelBuf[count];
|
||||
optionsEnumArray[count++] = static_cast<int>(presets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int initialSelection = 0;
|
||||
for (int i = 1; i < count; ++i) {
|
||||
if (optionsEnumArray[i] == static_cast<int>(config.lora.modem_preset)) {
|
||||
initialSelection = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Radio Preset";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||
bannerOptions.optionsCount = static_cast<uint8_t>(count);
|
||||
bannerOptions.InitialSelected = initialSelection;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == -1) {
|
||||
menuHandler::menuQueue = menuHandler::LoraMenu;
|
||||
screen->runNow();
|
||||
return;
|
||||
}
|
||||
config.lora.use_preset = true;
|
||||
config.lora.modem_preset = static_cast<meshtastic_Config_LoRaConfig_ModemPreset>(selected);
|
||||
config.lora.channel_num = 0; // Reset to default channel for the preset
|
||||
config.lora.override_frequency = 0; // Clear any custom frequency
|
||||
service->reloadConfig(SEGMENT_CONFIG);
|
||||
};
|
||||
return bannerOptions;
|
||||
}
|
||||
|
||||
void menuHandler::radioPresetPicker()
|
||||
{
|
||||
static const RadioPresetOption presetOptions[] = {
|
||||
{"Back", OptionsAction::Back},
|
||||
{"LongTurbo", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO},
|
||||
{"LongModerate", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE},
|
||||
{"LongFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST},
|
||||
{"MediumSlow", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW},
|
||||
{"MediumFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST},
|
||||
{"ShortSlow", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW},
|
||||
{"ShortFast", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST},
|
||||
{"ShortTurbo", OptionsAction::Select, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO},
|
||||
};
|
||||
|
||||
constexpr size_t presetCount = sizeof(presetOptions) / sizeof(presetOptions[0]);
|
||||
static std::array<const char *, presetCount> presetLabels{};
|
||||
|
||||
auto bannerOptions =
|
||||
createStaticBannerOptions("Radio Preset", presetOptions, presetLabels, [](const RadioPresetOption &option, int) -> void {
|
||||
if (option.action == OptionsAction::Back) {
|
||||
menuHandler::menuQueue = menuHandler::LoraMenu;
|
||||
screen->runNow();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!option.hasValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
config.lora.modem_preset = option.value;
|
||||
config.lora.channel_num = 0; // Reset to default channel for the preset
|
||||
config.lora.override_frequency = 0; // Clear any custom frequency
|
||||
service->reloadConfig(SEGMENT_CONFIG);
|
||||
});
|
||||
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
screen->showOverlayBanner(buildRegionPresetBanner());
|
||||
}
|
||||
|
||||
void menuHandler::twelveHourPicker()
|
||||
@@ -555,7 +581,7 @@ void menuHandler::TZPicker()
|
||||
|
||||
void menuHandler::clockMenu()
|
||||
{
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
static const char *optionsArray[] = {"Back", "Time Format", "Timezone"};
|
||||
#else
|
||||
static const char *optionsArray[] = {"Back", "Clock Face", "Time Format", "Timezone"};
|
||||
@@ -1307,6 +1333,11 @@ void menuHandler::positionBaseMenu()
|
||||
if (accelerometerThread) {
|
||||
accelerometerThread->calibrate(30);
|
||||
}
|
||||
#endif
|
||||
#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && !MESHTASTIC_EXCLUDE_MAGNETOMETER
|
||||
if (magnetometerThread) {
|
||||
magnetometerThread->calibrate(30);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case PositionAction::GPSSmartPosition:
|
||||
|
||||
@@ -26,7 +26,7 @@ extern bool haveGlyphs(const char *str);
|
||||
// Global screen instance
|
||||
extern graphics::Screen *screen;
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
static uint32_t lastSwitchTime = 0;
|
||||
#endif
|
||||
namespace graphics
|
||||
@@ -788,7 +788,7 @@ void drawDynamicListScreen_Nodes(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
display->clear();
|
||||
if (now - lastSwitchTime >= 3000) {
|
||||
display->display();
|
||||
@@ -824,7 +824,7 @@ void drawDynamicListScreen_Location(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
|
||||
unsigned long now = millis();
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
display->clear();
|
||||
if (now - lastSwitchTime >= 3000) {
|
||||
display->display();
|
||||
@@ -894,7 +894,7 @@ void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
double lat = DegD(ourSelfPos.latitude_i);
|
||||
double lon = DegD(ourSelfPos.longitude_i);
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
display->clear();
|
||||
uint32_t now = millis();
|
||||
if (now - lastSwitchTime >= 2000) {
|
||||
|
||||
@@ -728,7 +728,7 @@ void NotificationRenderer::drawNotificationBox(OLEDDisplay *display, OLEDDisplay
|
||||
}
|
||||
int16_t boxTop = (display->height() / 2) - (boxHeight / 2);
|
||||
boxHeight += (currentResolution == ScreenResolution::High) ? 2 : 1;
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
if (visibleTotalLines == 1) {
|
||||
boxTop += 25;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#if HAS_SCREEN
|
||||
#include "CompassRenderer.h"
|
||||
#include "GPSStatus.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "NodeListRenderer.h"
|
||||
@@ -25,7 +26,7 @@
|
||||
|
||||
// External variables
|
||||
extern graphics::Screen *screen;
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
static uint32_t lastSwitchTime = 0;
|
||||
#endif
|
||||
namespace graphics
|
||||
@@ -753,7 +754,7 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat
|
||||
if (!node || node->num == nodeDB->getNodeNum() || !nodeInfoLiteIsFavorite(node))
|
||||
return;
|
||||
display->clear();
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
uint32_t now = millis();
|
||||
if (now - lastSwitchTime >= 10000) // 10000 ms = 10 秒
|
||||
{
|
||||
@@ -816,16 +817,16 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat
|
||||
// Helper to get SNR limit based on modem preset
|
||||
auto getSnrLimit = [](meshtastic_Config_LoRaConfig_ModemPreset preset) -> float {
|
||||
switch (preset) {
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
|
||||
case PRESET(LONG_SLOW):
|
||||
case PRESET(LONG_MODERATE):
|
||||
case PRESET(LONG_FAST):
|
||||
return -6.0f;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
|
||||
case PRESET(MEDIUM_SLOW):
|
||||
case PRESET(MEDIUM_FAST):
|
||||
return -5.5f;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
|
||||
case PRESET(SHORT_SLOW):
|
||||
case PRESET(SHORT_FAST):
|
||||
case PRESET(SHORT_TURBO):
|
||||
return -4.5f;
|
||||
default:
|
||||
return -6.0f;
|
||||
@@ -959,7 +960,7 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat
|
||||
if (seenStr[0]) {
|
||||
display->drawString(x, getTextPositions(display)[line++], seenStr);
|
||||
}
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
#if !defined(OLED_TINY)
|
||||
// === 4. Uptime (only show if metric is present) ===
|
||||
char uptimeStr[32] = "";
|
||||
meshtastic_DeviceMetrics nodeMetrics;
|
||||
@@ -1172,7 +1173,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
line += 1;
|
||||
|
||||
// === Node Identity ===
|
||||
@@ -1456,7 +1457,7 @@ void UIRenderer::drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLED
|
||||
// needs to be drawn relative to x and y
|
||||
|
||||
// draw centered icon left to right and centered above the one line of app text
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
#if defined(OLED_TINY)
|
||||
display->drawXbm(x + (SCREEN_WIDTH - 50) / 2, y + (SCREEN_HEIGHT - 28) / 2, icon_width, icon_height, icon_bits);
|
||||
if (gBootSplashBoldPass) {
|
||||
display->drawXbm(x + (SCREEN_WIDTH - 50) / 2 + 1, y + (SCREEN_HEIGHT - 28) / 2, icon_width, icon_height, icon_bits);
|
||||
@@ -1666,7 +1667,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
||||
}
|
||||
display->drawString(x, textPos[line++], altitudeLine);
|
||||
}
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
#if !defined(OLED_TINY)
|
||||
// === Draw Compass ===
|
||||
if (validHeading || statusLine1) {
|
||||
// --- Compass Rendering: landscape (wide) screens use original side-aligned logic ---
|
||||
|
||||
@@ -318,7 +318,7 @@ const uint8_t chirpy_small[] = {0x7f, 0x41, 0x55, 0x55, 0x55, 0x55, 0x41, 0x7f};
|
||||
#define connection_icon_height 5
|
||||
const uint8_t connection_icon[] = {0x36, 0x41, 0x5D, 0x41, 0x36};
|
||||
|
||||
#ifdef M5STACK_UNITC6L
|
||||
#ifdef OLED_TINY
|
||||
#include "img/icon_small.xbm"
|
||||
#else
|
||||
#include "img/icon.xbm"
|
||||
|
||||
@@ -64,6 +64,8 @@ enum MenuAction {
|
||||
SET_REGION_KZ_863,
|
||||
SET_REGION_NP_865,
|
||||
SET_REGION_BR_902,
|
||||
SET_REGION_EU_866,
|
||||
SET_REGION_NARROW_868,
|
||||
// Device Roles
|
||||
SET_ROLE_CLIENT,
|
||||
SET_ROLE_CLIENT_MUTE,
|
||||
@@ -78,6 +80,11 @@ enum MenuAction {
|
||||
SET_PRESET_SHORT_SLOW,
|
||||
SET_PRESET_SHORT_FAST,
|
||||
SET_PRESET_SHORT_TURBO,
|
||||
SET_PRESET_LITE_SLOW,
|
||||
SET_PRESET_LITE_FAST,
|
||||
SET_PRESET_NARROW_SLOW,
|
||||
SET_PRESET_NARROW_FAST,
|
||||
SET_PRESET_FROM_REGION, // Dynamic: preset chosen from region-available list
|
||||
// Timezones
|
||||
SET_TZ_US_HAWAII,
|
||||
SET_TZ_US_ALASKA,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "DisplayFormatters.h"
|
||||
#include "GPS.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "MeshService.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
@@ -257,6 +258,11 @@ int32_t InkHUD::MenuApplet::runOnce()
|
||||
return OSThread::disable();
|
||||
}
|
||||
|
||||
// Storage for the dynamically-built region preset list — populated in showPage(NODE_CONFIG_PRESET)
|
||||
static constexpr uint8_t MAX_REGION_PRESETS = 16;
|
||||
static meshtastic_Config_LoRaConfig_ModemPreset regionPresets[MAX_REGION_PRESETS];
|
||||
static uint8_t regionPresetCount = 0;
|
||||
|
||||
static void applyLoRaRegion(meshtastic_Config_LoRaConfig_RegionCode region)
|
||||
{
|
||||
if (config.lora.region == region)
|
||||
@@ -276,7 +282,7 @@ static void applyLoRaRegion(meshtastic_Config_LoRaConfig_RegionCode region)
|
||||
|
||||
initRegion();
|
||||
|
||||
if (myRegion && myRegion->dutyCycle < 100) {
|
||||
if (myRegion && getEffectiveDutyCycle() < 100) {
|
||||
config.lora.ignore_mqtt = true;
|
||||
}
|
||||
|
||||
@@ -770,6 +776,14 @@ void InkHUD::MenuApplet::execute(MenuItem item)
|
||||
applyLoRaRegion(meshtastic_Config_LoRaConfig_RegionCode_BR_902);
|
||||
break;
|
||||
|
||||
case SET_REGION_EU_866:
|
||||
applyLoRaRegion(meshtastic_Config_LoRaConfig_RegionCode_EU_866);
|
||||
break;
|
||||
|
||||
case SET_REGION_NARROW_868:
|
||||
applyLoRaRegion(meshtastic_Config_LoRaConfig_RegionCode_EU_N_868);
|
||||
break;
|
||||
|
||||
// Roles
|
||||
case SET_ROLE_CLIENT:
|
||||
applyDeviceRole(meshtastic_Config_DeviceConfig_Role_CLIENT);
|
||||
@@ -789,37 +803,46 @@ void InkHUD::MenuApplet::execute(MenuItem item)
|
||||
|
||||
// Presets
|
||||
case SET_PRESET_LONG_SLOW:
|
||||
applyLoRaPreset(meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW);
|
||||
applyLoRaPreset(PRESET(LONG_SLOW));
|
||||
break;
|
||||
|
||||
case SET_PRESET_LONG_MODERATE:
|
||||
applyLoRaPreset(meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE);
|
||||
applyLoRaPreset(PRESET(LONG_MODERATE));
|
||||
break;
|
||||
|
||||
case SET_PRESET_LONG_FAST:
|
||||
applyLoRaPreset(meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST);
|
||||
applyLoRaPreset(PRESET(LONG_FAST));
|
||||
break;
|
||||
|
||||
case SET_PRESET_MEDIUM_SLOW:
|
||||
applyLoRaPreset(meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW);
|
||||
applyLoRaPreset(PRESET(MEDIUM_SLOW));
|
||||
break;
|
||||
|
||||
case SET_PRESET_MEDIUM_FAST:
|
||||
applyLoRaPreset(meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST);
|
||||
applyLoRaPreset(PRESET(MEDIUM_FAST));
|
||||
break;
|
||||
|
||||
case SET_PRESET_SHORT_SLOW:
|
||||
applyLoRaPreset(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW);
|
||||
applyLoRaPreset(PRESET(SHORT_SLOW));
|
||||
break;
|
||||
|
||||
case SET_PRESET_SHORT_FAST:
|
||||
applyLoRaPreset(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST);
|
||||
applyLoRaPreset(PRESET(SHORT_FAST));
|
||||
break;
|
||||
|
||||
case SET_PRESET_SHORT_TURBO:
|
||||
applyLoRaPreset(meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO);
|
||||
applyLoRaPreset(PRESET(SHORT_TURBO));
|
||||
break;
|
||||
|
||||
case SET_PRESET_FROM_REGION: {
|
||||
// cursor - 1 because index 0 is "Back"
|
||||
const uint8_t index = cursor - 1;
|
||||
if (index < regionPresetCount) {
|
||||
applyLoRaPreset(regionPresets[index]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Timezones
|
||||
case SET_TZ_US_HAWAII:
|
||||
applyTimezone("HST10");
|
||||
@@ -1421,6 +1444,8 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
|
||||
items.push_back(MenuItem("US", MenuAction::SET_REGION_US, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("EU 868", MenuAction::SET_REGION_EU_868, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("EU 433", MenuAction::SET_REGION_EU_433, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("EU 866", MenuAction::SET_REGION_EU_866, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("EU 868 Narrow", MenuAction::SET_REGION_NARROW_868, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("CN", MenuAction::SET_REGION_CN, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("JP", MenuAction::SET_REGION_JP, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("ANZ", MenuAction::SET_REGION_ANZ, MenuPage::EXIT));
|
||||
@@ -1450,13 +1475,17 @@ void InkHUD::MenuApplet::showPage(MenuPage page)
|
||||
case NODE_CONFIG_PRESET: {
|
||||
previousPage = MenuPage::NODE_CONFIG_LORA;
|
||||
items.push_back(MenuItem("Back", previousPage));
|
||||
items.push_back(MenuItem("Long Moderate", MenuAction::SET_PRESET_LONG_MODERATE, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("Long Fast", MenuAction::SET_PRESET_LONG_FAST, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("Medium Slow", MenuAction::SET_PRESET_MEDIUM_SLOW, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("Medium Fast", MenuAction::SET_PRESET_MEDIUM_FAST, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("Short Slow", MenuAction::SET_PRESET_SHORT_SLOW, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("Short Fast", MenuAction::SET_PRESET_SHORT_FAST, MenuPage::EXIT));
|
||||
items.push_back(MenuItem("Short Turbo", MenuAction::SET_PRESET_SHORT_TURBO, MenuPage::EXIT));
|
||||
regionPresetCount = 0;
|
||||
if (myRegion && myRegion->profile) {
|
||||
const meshtastic_Config_LoRaConfig_ModemPreset *presets = myRegion->getAvailablePresets();
|
||||
size_t numPresets = myRegion->getNumPresets();
|
||||
for (size_t i = 0; i < numPresets && regionPresetCount < MAX_REGION_PRESETS; ++i) {
|
||||
regionPresets[regionPresetCount++] = presets[i];
|
||||
const char *name = DisplayFormatters::getModemPresetDisplayName(presets[i], false, true);
|
||||
nodeConfigLabels.emplace_back(name);
|
||||
items.push_back(MenuItem(nodeConfigLabels.back().c_str(), MenuAction::SET_PRESET_FROM_REGION, MenuPage::EXIT));
|
||||
}
|
||||
}
|
||||
items.push_back(MenuItem("Exit", MenuPage::EXIT));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -53,10 +53,11 @@ void InkHUD::MessageStore::saveToFlash()
|
||||
f.write(reinterpret_cast<const uint8_t *>(&m.timestamp), sizeof(m.timestamp)); // Write timestamp. 4 bytes
|
||||
f.write(reinterpret_cast<const uint8_t *>(&m.sender), sizeof(m.sender)); // Write sender NodeId. 4 Bytes
|
||||
f.write(reinterpret_cast<const uint8_t *>(&m.channelIndex), sizeof(m.channelIndex)); // Write channel index. 1 Byte
|
||||
f.write(reinterpret_cast<const uint8_t *>(m.text.c_str()), min(MAX_MESSAGE_SIZE, m.text.size())); // Write message text
|
||||
f.write('\0'); // Append null term
|
||||
LOG_DEBUG("Wrote message %u, length %u, text \"%s\"", static_cast<uint32_t>(i), min(MAX_MESSAGE_SIZE, m.text.size()),
|
||||
m.text.c_str());
|
||||
f.write(reinterpret_cast<const uint8_t *>(m.text.c_str()),
|
||||
min((size_t)MAX_MESSAGE_SIZE, m.text.size())); // Write message text
|
||||
f.write('\0'); // Append null term
|
||||
LOG_DEBUG("Wrote message %u, length %u, text \"%s\"", static_cast<uint32_t>(i),
|
||||
min((size_t)MAX_MESSAGE_SIZE, m.text.size()), m.text.c_str());
|
||||
}
|
||||
|
||||
// Release firmware's SPI lock, because SafeFile::close needs it
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|
||||
#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE
|
||||
|
||||
// REVISIT esp_adc_cal.h
|
||||
// "legacy adc calibration driver is deprecated, please migrate to use esp_adc/adc_cali.h and esp_adc/adc_cali_scheme.h"
|
||||
#include <esp_adc_cal.h>
|
||||
#include <soc/adc_channel.h>
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ TLoraPagerKeyboard::TLoraPagerKeyboard()
|
||||
: TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(UINT8_MAX),
|
||||
next_key(UINT8_MAX), last_tap(0L), char_idx(0), tap_interval(0)
|
||||
{
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
ledcAttach(KB_BL_PIN, LEDC_BACKLIGHT_FREQ, LEDC_BACKLIGHT_BIT_WIDTH);
|
||||
#else
|
||||
ledcSetup(LEDC_BACKLIGHT_CHANNEL, LEDC_BACKLIGHT_FREQ, LEDC_BACKLIGHT_BIT_WIDTH);
|
||||
@@ -108,7 +108,7 @@ void TLoraPagerKeyboard::setBacklight(bool on)
|
||||
uint32_t _brightness = 0;
|
||||
if (on)
|
||||
_brightness = brightness;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
||||
ledcWrite(KB_BL_PIN, _brightness);
|
||||
#else
|
||||
ledcWrite(LEDC_BACKLIGHT_CHANNEL, _brightness);
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#if !MESHTASTIC_EXCLUDE_GPS
|
||||
#include "GPS.h"
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_INPUTBROKER
|
||||
#include "input/InputBroker.h"
|
||||
#endif
|
||||
#include "MeshRadio.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
@@ -59,6 +62,12 @@ NimbleBluetooth *nimbleBluetooth = nullptr;
|
||||
NRF52Bluetooth *nrf52Bluetooth = nullptr;
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_NRF54L15
|
||||
void nrf54l15Setup();
|
||||
void nrf54l15Loop();
|
||||
NRF54L15Bluetooth *nrf54l15Bluetooth = nullptr;
|
||||
#endif
|
||||
|
||||
#if HAS_WIFI || defined(USE_WS5500) || defined(USE_CH390D)
|
||||
#include "mesh/api/WiFiServerAPI.h"
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
@@ -127,6 +136,10 @@ void printPartitionTable()
|
||||
#include "motion/AccelerometerThread.h"
|
||||
AccelerometerThread *accelerometerThread = nullptr;
|
||||
#endif
|
||||
#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && !MESHTASTIC_EXCLUDE_MAGNETOMETER
|
||||
#include "motion/MagnetometerThread.h"
|
||||
MagnetometerThread *magnetometerThread = nullptr;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_I2S
|
||||
#include "AudioThread.h"
|
||||
@@ -197,6 +210,8 @@ bool osk_found = false;
|
||||
ScanI2C::DeviceAddress rtc_found = ScanI2C::ADDRESS_NONE;
|
||||
// The I2C address of the Accelerometer (if found)
|
||||
ScanI2C::DeviceAddress accelerometer_found = ScanI2C::ADDRESS_NONE;
|
||||
// The I2C address of the Magnetometer (if found)
|
||||
ScanI2C::DeviceAddress magnetometer_found = ScanI2C::ADDRESS_NONE;
|
||||
// The I2C address of the RGB LED (if found)
|
||||
ScanI2C::FoundDevice rgb_found = ScanI2C::FoundDevice(ScanI2C::DeviceType::NONE, ScanI2C::ADDRESS_NONE);
|
||||
/// The I2C address of our Air Quality Indicator (if found)
|
||||
@@ -422,6 +437,11 @@ void setup()
|
||||
digitalWrite(VEXT_ENABLE, VEXT_ON_VALUE); // turn on the display power
|
||||
#endif
|
||||
|
||||
#if defined(PIN_SENSOR_EN)
|
||||
pinMode(PIN_SENSOR_EN, OUTPUT);
|
||||
digitalWrite(PIN_SENSOR_EN, PIN_SENSOR_EN_ACTIVE); // turn on sensor power
|
||||
#endif
|
||||
|
||||
#if defined(BIAS_T_ENABLE)
|
||||
pinMode(BIAS_T_ENABLE, OUTPUT);
|
||||
digitalWrite(BIAS_T_ENABLE, BIAS_T_VALUE); // turn on 5V for GPS Antenna
|
||||
@@ -662,6 +682,11 @@ void setup()
|
||||
accelerometer_found = acc_info.type != ScanI2C::DeviceType::NONE ? acc_info.address : accelerometer_found;
|
||||
LOG_DEBUG("acc_info = %i", acc_info.type);
|
||||
#endif
|
||||
#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_MAGNETOMETER
|
||||
auto mag_info = i2cScanner->firstMagnetometer();
|
||||
magnetometer_found = mag_info.type != ScanI2C::DeviceType::NONE ? mag_info.address : magnetometer_found;
|
||||
LOG_DEBUG("mag_info = %i", mag_info.type);
|
||||
#endif
|
||||
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260);
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::INA226, meshtastic_TelemetrySensorType_INA226);
|
||||
@@ -674,6 +699,8 @@ void setup()
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMI8658, meshtastic_TelemetrySensorType_QMI8658);
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::QMC5883L, meshtastic_TelemetrySensorType_QMC5883L);
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::HMC5883L, meshtastic_TelemetrySensorType_QMC5883L);
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MMC5983MA, meshtastic_TelemetrySensorType_MMC5983MA);
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM42607P, meshtastic_TelemetrySensorType_ICM42607P);
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MLX90614, meshtastic_TelemetrySensorType_MLX90614);
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948);
|
||||
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102);
|
||||
@@ -696,6 +723,9 @@ void setup()
|
||||
#ifdef ARCH_NRF52
|
||||
nrf52Setup();
|
||||
#endif
|
||||
#ifdef ARCH_NRF54L15
|
||||
nrf54l15Setup();
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_RP2040
|
||||
rp2040Setup();
|
||||
@@ -741,6 +771,11 @@ void setup()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef OLED_GEOMETRY_OVERRIDE
|
||||
// Per-variant geometry (e.g. 72x40 micro-OLEDs). Takes precedence over the
|
||||
// default GEOMETRY_128_64 set at the top of setup().
|
||||
screen_geometry = OLED_GEOMETRY_OVERRIDE;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_I2C
|
||||
@@ -749,6 +784,11 @@ void setup()
|
||||
accelerometerThread = new AccelerometerThread(acc_info.type);
|
||||
}
|
||||
#endif
|
||||
#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_MAGNETOMETER
|
||||
if (mag_info.type != ScanI2C::DeviceType::NONE) {
|
||||
magnetometerThread = new MagnetometerThread(mag_info.type);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAS_NEOPIXEL) || defined(UNPHONE) || defined(RGBLED_RED)
|
||||
ambientLightingThread = new AmbientLightingThread(ScanI2C::DeviceType::NONE);
|
||||
@@ -1126,6 +1166,9 @@ void loop()
|
||||
#endif
|
||||
#ifdef ARCH_NRF52
|
||||
nrf52Loop();
|
||||
#endif
|
||||
#ifdef ARCH_NRF54L15
|
||||
nrf54l15Loop();
|
||||
#endif
|
||||
power->powerCommandsCheck();
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ extern NimbleBluetooth *nimbleBluetooth;
|
||||
#include "NRF52Bluetooth.h"
|
||||
extern NRF52Bluetooth *nrf52Bluetooth;
|
||||
#endif
|
||||
#ifdef ARCH_NRF54L15
|
||||
#include "NRF54L15Bluetooth.h"
|
||||
extern NRF54L15Bluetooth *nrf54l15Bluetooth;
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_I2C
|
||||
#include "detect/ScanI2CTwoWire.h"
|
||||
#endif
|
||||
@@ -35,6 +39,7 @@ extern bool kb_found;
|
||||
extern bool osk_found;
|
||||
extern ScanI2C::DeviceAddress rtc_found;
|
||||
extern ScanI2C::DeviceAddress accelerometer_found;
|
||||
extern ScanI2C::DeviceAddress magnetometer_found;
|
||||
extern ScanI2C::FoundDevice rgb_found;
|
||||
extern ScanI2C::DeviceAddress aqi_found;
|
||||
|
||||
@@ -69,6 +74,10 @@ extern graphics::Screen *screen;
|
||||
#include "motion/AccelerometerThread.h"
|
||||
extern AccelerometerThread *accelerometerThread;
|
||||
#endif
|
||||
#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && !MESHTASTIC_EXCLUDE_MAGNETOMETER
|
||||
#include "motion/MagnetometerThread.h"
|
||||
extern MagnetometerThread *magnetometerThread;
|
||||
#endif
|
||||
|
||||
extern bool isVibrating;
|
||||
|
||||
|
||||
@@ -93,6 +93,23 @@ void Channels::initDefaultLoraConfig()
|
||||
#ifdef USERPREFS_LORACONFIG_CHANNEL_NUM
|
||||
loraConfig.channel_num = USERPREFS_LORACONFIG_CHANNEL_NUM;
|
||||
#endif
|
||||
|
||||
// Apply any hardcoded USERPREFS overrides for custom modem config (e.g. region-locked boards)
|
||||
#ifdef USERPREFS_LORACONFIG_USE_PRESET
|
||||
loraConfig.use_preset = USERPREFS_LORACONFIG_USE_PRESET;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_BANDWIDTH
|
||||
loraConfig.bandwidth = USERPREFS_LORACONFIG_BANDWIDTH;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_SPREAD_FACTOR
|
||||
loraConfig.spread_factor = USERPREFS_LORACONFIG_SPREAD_FACTOR;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_CODING_RATE
|
||||
loraConfig.coding_rate = USERPREFS_LORACONFIG_CODING_RATE;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_OVERRIDE_FREQUENCY
|
||||
loraConfig.override_frequency = USERPREFS_LORACONFIG_OVERRIDE_FREQUENCY;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Channels::ensureLicensedOperation()
|
||||
|
||||
@@ -60,6 +60,26 @@ uint32_t Default::getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t d
|
||||
return base * coef;
|
||||
}
|
||||
|
||||
uint32_t Default::getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes,
|
||||
TrafficType type)
|
||||
{
|
||||
uint32_t baseMs = getConfiguredOrDefaultMsScaled(configured, defaultValue, numOnlineNodes);
|
||||
|
||||
if (!myRegion || !myRegion->profile)
|
||||
return baseMs;
|
||||
|
||||
int8_t throttle =
|
||||
(type == TrafficType::POSITION) ? myRegion->profile->positionThrottle : myRegion->profile->telemetryThrottle;
|
||||
|
||||
// throttle <= 0 means unset; 1 is the neutral multiplier — skip the multiply for performance
|
||||
if (throttle <= 1)
|
||||
return baseMs;
|
||||
|
||||
constexpr uint32_t MAX_MS = static_cast<uint32_t>(INT32_MAX);
|
||||
uint64_t result = static_cast<uint64_t>(baseMs) * static_cast<uint64_t>(throttle);
|
||||
return result >= static_cast<uint64_t>(MAX_MS) ? MAX_MS : static_cast<uint32_t>(result);
|
||||
}
|
||||
|
||||
uint32_t Default::getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue)
|
||||
{
|
||||
// If zero, intervals should be coalesced later by getConfiguredOrDefault... methods
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#define min_neighbor_info_broadcast_secs 4 * 60 * 60
|
||||
#define default_map_publish_interval_secs 60 * 60
|
||||
|
||||
enum class TrafficType { POSITION, TELEMETRY };
|
||||
|
||||
// Traffic management defaults
|
||||
#define default_traffic_mgmt_position_precision_bits 24 // ~10m grid cells
|
||||
#define default_traffic_mgmt_position_min_interval_secs (ONE_DAY / 2) // 12 hours between identical positions
|
||||
@@ -64,6 +66,8 @@ class Default
|
||||
// Note: numOnlineNodes uses uint32_t to match the public API and allow flexibility,
|
||||
// even though internal node counts use uint16_t (max 65535 nodes)
|
||||
static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes);
|
||||
static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes,
|
||||
TrafficType type);
|
||||
static uint8_t getConfiguredOrDefaultHopLimit(uint8_t configured);
|
||||
static uint32_t getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue);
|
||||
|
||||
|
||||
@@ -368,4 +368,10 @@ template <typename T> bool LR11x0Interface<T>::sleep()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> int16_t LR11x0Interface<T>::getCurrentRSSI()
|
||||
{
|
||||
float rssi = lora.getRSSI(false, true);
|
||||
return (int16_t)round(rssi);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -37,6 +37,8 @@ template <class T> class LR11x0Interface : public RadioLibInterface
|
||||
*/
|
||||
T lora;
|
||||
|
||||
int16_t getCurrentRSSI() override;
|
||||
|
||||
/**
|
||||
* Glue functions called from ISR land
|
||||
*/
|
||||
|
||||
@@ -136,17 +136,6 @@ template <typename T> bool LR20x0Interface<T>::init()
|
||||
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED)
|
||||
return false;
|
||||
|
||||
// Some basic info about the module's explicit firmware version - no other info available
|
||||
// Currently requires radiolib godmode
|
||||
|
||||
#if RADIOLIB_GODMODE
|
||||
uint8_t fwMajor = 0;
|
||||
uint8_t fwMinor = 0;
|
||||
int versionRes = lora.getVersion(&fwMajor, &fwMinor);
|
||||
if (versionRes == RADIOLIB_ERR_NONE)
|
||||
LOG_DEBUG("LR20x0 FW %d.%d", fwMajor, fwMinor);
|
||||
#endif
|
||||
|
||||
LOG_INFO("Frequency set to %f", getFreq());
|
||||
LOG_INFO("Bandwidth set to %f", bw);
|
||||
LOG_INFO("Power output set to %d", power);
|
||||
@@ -154,17 +143,6 @@ template <typename T> bool LR20x0Interface<T>::init()
|
||||
if (res == RADIOLIB_ERR_NONE)
|
||||
res = lora.setCRC(2);
|
||||
|
||||
// Standard DCDC ramp timing from RadioLib workarounds (register 0x00F20024)
|
||||
// Currently requires radiolib godmode
|
||||
#if RADIOLIB_GODMODE
|
||||
if (res == RADIOLIB_ERR_NONE) {
|
||||
uint8_t rampTimes[4] = {15, 15, 15, 15}; // Standard case for all conditions
|
||||
res = lora.setRegMode(RADIOLIB_LR2021_REG_MODE_SIMO_NORMAL, rampTimes);
|
||||
if (res != RADIOLIB_ERR_NONE)
|
||||
LOG_WARN("LR2021 setRegMode failed: %d", res);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LR2021_DIO_AS_RF_SWITCH
|
||||
bool dioAsRfSwitch = true;
|
||||
#elif defined(ARCH_PORTDUINO)
|
||||
@@ -396,4 +374,10 @@ template <typename T> bool LR20x0Interface<T>::sleep()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> int16_t LR20x0Interface<T>::getCurrentRSSI()
|
||||
{
|
||||
float rssi = lora.getRSSI(false, true);
|
||||
return (int16_t)round(rssi);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -37,6 +37,8 @@ template <class T> class LR20x0Interface : public RadioLibInterface
|
||||
*/
|
||||
T lora;
|
||||
|
||||
int16_t getCurrentRSSI() override;
|
||||
|
||||
/**
|
||||
* Glue functions called from ISR land
|
||||
*/
|
||||
|
||||
+50
-14
@@ -13,9 +13,16 @@
|
||||
static const meshtastic_Config_LoRaConfig_ModemPreset MODEM_PRESET_END =
|
||||
static_cast<meshtastic_Config_LoRaConfig_ModemPreset>(0xFF);
|
||||
|
||||
#define PRESET(name) meshtastic_Config_LoRaConfig_ModemPreset_##name
|
||||
|
||||
// Override slot magic numbers for RegionInfo.overrideSlot
|
||||
#define OVERRIDE_SLOT_DEFAULT_CHANNEL_HASH 0 // Use hash of primary channel name
|
||||
#define OVERRIDE_SLOT_PRESET_HASH -1 // Use hash of preset name instead
|
||||
// Positive values (1-32767) are explicit slot numbers
|
||||
|
||||
// Region profile: bundles the preset list with regulatory parameters shared across regions
|
||||
struct RegionProfile {
|
||||
const meshtastic_Config_LoRaConfig_ModemPreset *presets; // sentinel-terminated; first entry is the default
|
||||
const meshtastic_Config_LoRaConfig_ModemPreset *presets; // sentinel-terminated
|
||||
float spacing; // gaps between radio channels
|
||||
float padding; // padding at each side of the "operating channel"
|
||||
bool audioPermitted;
|
||||
@@ -23,14 +30,20 @@ struct RegionProfile {
|
||||
int8_t textThrottle; // throttle for text - future expansion
|
||||
int8_t positionThrottle; // throttle for location data - future expansion
|
||||
int8_t telemetryThrottle; // throttle for telemetry - future expansion
|
||||
uint8_t overrideSlot; // a per-region override slot for if we need to fix it in place
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the effective duty cycle for the current region based on device role.
|
||||
* For EU_866, returns 10% for fixed devices (ROUTER, ROUTER_LATE) and 2.5% for mobile devices.
|
||||
* For other regions, returns the standard duty cycle.
|
||||
*/
|
||||
extern float getEffectiveDutyCycle();
|
||||
|
||||
extern const RegionProfile PROFILE_STD;
|
||||
extern const RegionProfile PROFILE_EU868;
|
||||
extern const RegionProfile PROFILE_UNDEF;
|
||||
// extern const RegionProfile PROFILE_LITE;
|
||||
// extern const RegionProfile PROFILE_NARROW;
|
||||
extern const RegionProfile PROFILE_LITE;
|
||||
extern const RegionProfile PROFILE_NARROW;
|
||||
// extern const RegionProfile PROFILE_HAM;
|
||||
|
||||
// Map from old region names to new region enums
|
||||
@@ -43,10 +56,13 @@ struct RegionInfo {
|
||||
bool freqSwitching;
|
||||
bool wideLora;
|
||||
const RegionProfile *profile;
|
||||
const char *name; // EU433 etc
|
||||
meshtastic_Config_LoRaConfig_ModemPreset defaultPreset;
|
||||
int16_t overrideSlot; // per-region override slot for frequency selection
|
||||
// Magic values: 0 = use channel name hash, -1 = use preset name hash, >0 = explicit slot
|
||||
const char *name; // EU433 etc
|
||||
|
||||
// Preset accessors (delegate through profile)
|
||||
meshtastic_Config_LoRaConfig_ModemPreset getDefaultPreset() const { return profile->presets[0]; }
|
||||
meshtastic_Config_LoRaConfig_ModemPreset getDefaultPreset() const { return defaultPreset; }
|
||||
const meshtastic_Config_LoRaConfig_ModemPreset *getAvailablePresets() const { return profile->presets; }
|
||||
size_t getNumPresets() const
|
||||
{
|
||||
@@ -143,46 +159,66 @@ static inline void modemPresetToParams(meshtastic_Config_LoRaConfig_ModemPreset
|
||||
uint8_t &cr)
|
||||
{
|
||||
switch (preset) {
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
|
||||
case PRESET(SHORT_TURBO):
|
||||
bwKHz = wideLora ? 1625.0f : 500.0f;
|
||||
cr = 5;
|
||||
sf = 7;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
|
||||
case PRESET(SHORT_FAST):
|
||||
bwKHz = wideLora ? 812.5f : 250.0f;
|
||||
cr = 5;
|
||||
sf = 7;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
|
||||
case PRESET(SHORT_SLOW):
|
||||
bwKHz = wideLora ? 812.5f : 250.0f;
|
||||
cr = 5;
|
||||
sf = 8;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
|
||||
case PRESET(MEDIUM_FAST):
|
||||
bwKHz = wideLora ? 812.5f : 250.0f;
|
||||
cr = 5;
|
||||
sf = 9;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
|
||||
case PRESET(MEDIUM_SLOW):
|
||||
bwKHz = wideLora ? 812.5f : 250.0f;
|
||||
cr = 5;
|
||||
sf = 10;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO:
|
||||
case PRESET(LONG_TURBO):
|
||||
bwKHz = wideLora ? 1625.0f : 500.0f;
|
||||
cr = 8;
|
||||
sf = 11;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
|
||||
case PRESET(LONG_MODERATE):
|
||||
bwKHz = wideLora ? 406.25f : 125.0f;
|
||||
cr = 8;
|
||||
sf = 11;
|
||||
break;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
|
||||
case PRESET(LONG_SLOW):
|
||||
bwKHz = wideLora ? 406.25f : 125.0f;
|
||||
cr = 8;
|
||||
sf = 12;
|
||||
break;
|
||||
case PRESET(LITE_FAST):
|
||||
bwKHz = 125;
|
||||
cr = 5;
|
||||
sf = 9;
|
||||
break;
|
||||
case PRESET(LITE_SLOW):
|
||||
bwKHz = 125;
|
||||
cr = 5;
|
||||
sf = 10;
|
||||
break;
|
||||
case PRESET(NARROW_FAST):
|
||||
bwKHz = 62.5f;
|
||||
cr = 6;
|
||||
sf = 7;
|
||||
break;
|
||||
case PRESET(NARROW_SLOW):
|
||||
bwKHz = 62.5f;
|
||||
cr = 6;
|
||||
sf = 8;
|
||||
break;
|
||||
default: // LONG_FAST (or illegal)
|
||||
bwKHz = wideLora ? 812.5f : 250.0f;
|
||||
cr = 5;
|
||||
|
||||
@@ -334,7 +334,9 @@ void MeshService::sendToPhone(meshtastic_MeshPacket *p)
|
||||
|
||||
if (toPhoneQueue.enqueue(p, 0) == false) {
|
||||
LOG_CRIT("Failed to queue a packet into toPhoneQueue!");
|
||||
abort();
|
||||
releaseToPool(p);
|
||||
fromNum++; // notify observers so phone can resync
|
||||
return;
|
||||
}
|
||||
fromNum++;
|
||||
}
|
||||
@@ -351,7 +353,8 @@ void MeshService::sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage
|
||||
|
||||
if (toPhoneMqttProxyQueue.enqueue(m, 0) == false) {
|
||||
LOG_CRIT("Failed to queue a packet into toPhoneMqttProxyQueue!");
|
||||
abort();
|
||||
releaseMqttClientProxyMessageToPool(m);
|
||||
return;
|
||||
}
|
||||
fromNum++;
|
||||
}
|
||||
@@ -383,7 +386,8 @@ void MeshService::sendClientNotification(meshtastic_ClientNotification *n)
|
||||
|
||||
if (toPhoneClientNotificationQueue.enqueue(n, 0) == false) {
|
||||
LOG_CRIT("Failed to queue a notification into toPhoneClientNotificationQueue!");
|
||||
abort();
|
||||
releaseClientNotificationToPool(n);
|
||||
return;
|
||||
}
|
||||
fromNum++;
|
||||
}
|
||||
|
||||
+60
-2
@@ -393,6 +393,13 @@ NodeDB::NodeDB()
|
||||
} else {
|
||||
LOG_WARN("Failed to read unique id from efuse");
|
||||
}
|
||||
#elif defined(ARCH_NRF54L15)
|
||||
// nRF54L15: DEVICEID is under FICR->INFO sub-struct (not top-level as on nRF52)
|
||||
uint64_t device_id_start = ((uint64_t)NRF_FICR->INFO.DEVICEID[1] << 32) | NRF_FICR->INFO.DEVICEID[0];
|
||||
uint64_t device_id_end = ((uint64_t)NRF_FICR->DEVICEADDR[1] << 32) | NRF_FICR->DEVICEADDR[0];
|
||||
memcpy(myNodeInfo.device_id.bytes, &device_id_start, sizeof(device_id_start));
|
||||
memcpy(myNodeInfo.device_id.bytes + sizeof(device_id_start), &device_id_end, sizeof(device_id_end));
|
||||
myNodeInfo.device_id.size = 16;
|
||||
#elif defined(ARCH_NRF52)
|
||||
// Nordic applies a FIPS compliant Random ID to each chip at the factory
|
||||
// We concatenate the device address to the Random ID to create a unique ID for now
|
||||
@@ -826,6 +833,23 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
||||
config.lora.modem_preset = USERPREFS_LORACONFIG_MODEM_PRESET;
|
||||
#else
|
||||
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_USE_PRESET
|
||||
config.lora.use_preset = USERPREFS_LORACONFIG_USE_PRESET;
|
||||
#else
|
||||
config.lora.use_preset = true;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_BANDWIDTH
|
||||
config.lora.bandwidth = USERPREFS_LORACONFIG_BANDWIDTH;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_SPREAD_FACTOR
|
||||
config.lora.spread_factor = USERPREFS_LORACONFIG_SPREAD_FACTOR;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_CODING_RATE
|
||||
config.lora.coding_rate = USERPREFS_LORACONFIG_CODING_RATE;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_OVERRIDE_FREQUENCY
|
||||
config.lora.override_frequency = USERPREFS_LORACONFIG_OVERRIDE_FREQUENCY;
|
||||
#endif
|
||||
config.lora.hop_limit = HOP_RELIABLE;
|
||||
#ifdef USERPREFS_CONFIG_LORA_IGNORE_MQTT
|
||||
@@ -961,6 +985,10 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
||||
config.network.wifi_enabled = USERPREFS_NETWORK_WIFI_ENABLED;
|
||||
#endif
|
||||
|
||||
#if USE_ETHERNET_DEFAULT
|
||||
config.network.eth_enabled = true;
|
||||
#endif
|
||||
|
||||
#ifdef USERPREFS_NETWORK_WIFI_SSID
|
||||
strncpy(config.network.wifi_ssid, USERPREFS_NETWORK_WIFI_SSID, sizeof(config.network.wifi_ssid));
|
||||
#endif
|
||||
@@ -1043,7 +1071,8 @@ void NodeDB::installDefaultModuleConfig()
|
||||
moduleConfig.has_store_forward = true;
|
||||
moduleConfig.has_telemetry = true;
|
||||
moduleConfig.has_external_notification = true;
|
||||
#if defined(PIN_BUZZER) || defined(PIN_VIBRATION) || defined(LED_NOTIFICATION) || defined(PCA_LED_NOTIFICATION)
|
||||
#if defined(PIN_BUZZER) || defined(PIN_VIBRATION) || defined(LED_NOTIFICATION) || defined(PCA_LED_NOTIFICATION) || \
|
||||
defined(NEOPIXEL_STATUS_NOTIFICATION_PIN)
|
||||
moduleConfig.external_notification.enabled = true;
|
||||
#endif
|
||||
#if defined(PIN_BUZZER)
|
||||
@@ -1064,7 +1093,7 @@ void NodeDB::installDefaultModuleConfig()
|
||||
#endif
|
||||
#if defined(PIN_VIBRATION)
|
||||
moduleConfig.external_notification.nag_timeout = 2;
|
||||
#elif defined(PIN_BUZZER) || defined(LED_NOTIFICATION)
|
||||
#elif defined(PIN_BUZZER) || defined(LED_NOTIFICATION) || defined(NEOPIXEL_STATUS_NOTIFICATION_PIN)
|
||||
moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;
|
||||
#endif
|
||||
|
||||
@@ -1217,7 +1246,14 @@ void NodeDB::initModuleConfigIntervals()
|
||||
#else
|
||||
moduleConfig.telemetry.device_update_interval = MAX_INTERVAL;
|
||||
#endif
|
||||
#ifdef USERPREFS_CONFIG_ENV_TELEM_UPDATE_INTERVAL
|
||||
moduleConfig.telemetry.environment_update_interval = USERPREFS_CONFIG_ENV_TELEM_UPDATE_INTERVAL;
|
||||
#else
|
||||
moduleConfig.telemetry.environment_update_interval = 0;
|
||||
#endif
|
||||
#ifdef USERPREFS_CONFIG_ENVIRONMENT_MEASUREMENT_ENABLED
|
||||
moduleConfig.telemetry.environment_measurement_enabled = USERPREFS_CONFIG_ENVIRONMENT_MEASUREMENT_ENABLED;
|
||||
#endif
|
||||
moduleConfig.telemetry.air_quality_interval = 0;
|
||||
moduleConfig.telemetry.power_update_interval = 0;
|
||||
moduleConfig.telemetry.health_update_interval = 0;
|
||||
@@ -1726,6 +1762,28 @@ void NodeDB::loadFromDisk()
|
||||
config.lora.tx_enabled = false;
|
||||
#endif
|
||||
|
||||
// Always-apply LoRa overrides: applied after loading saved config so they
|
||||
// take effect even when NVS already has a valid config (e.g. region-locked
|
||||
// dev boards with no BLE/serial to set the region at runtime).
|
||||
#ifdef USERPREFS_CONFIG_LORA_REGION
|
||||
config.lora.region = USERPREFS_CONFIG_LORA_REGION;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_USE_PRESET
|
||||
config.lora.use_preset = USERPREFS_LORACONFIG_USE_PRESET;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_BANDWIDTH
|
||||
config.lora.bandwidth = USERPREFS_LORACONFIG_BANDWIDTH;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_SPREAD_FACTOR
|
||||
config.lora.spread_factor = USERPREFS_LORACONFIG_SPREAD_FACTOR;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_CODING_RATE
|
||||
config.lora.coding_rate = USERPREFS_LORACONFIG_CODING_RATE;
|
||||
#endif
|
||||
#ifdef USERPREFS_LORACONFIG_OVERRIDE_FREQUENCY
|
||||
config.lora.override_frequency = USERPREFS_LORACONFIG_OVERRIDE_FREQUENCY;
|
||||
#endif
|
||||
|
||||
if (backupSecurity.private_key.size > 0) {
|
||||
LOG_DEBUG("Restoring backup of security config");
|
||||
config.security = backupSecurity;
|
||||
|
||||
@@ -71,7 +71,15 @@ void PhoneAPI::handleStartConfig()
|
||||
}
|
||||
pauseBluetoothLogging = true;
|
||||
spiLock->lock();
|
||||
#if defined(MESHTASTIC_EXCLUDE_FILES_MANIFEST)
|
||||
// Skip the recursive FS walk. Used by platforms whose Zephyr LittleFS
|
||||
// backend can't safely traverse a deep tree (e.g. nRF54L15) and platforms
|
||||
// that don't support OTA browsing — the manifest is only consumed by
|
||||
// companion apps for those flows.
|
||||
filesManifest.clear();
|
||||
#else
|
||||
filesManifest = getFiles("/", 10);
|
||||
#endif
|
||||
spiLock->unlock();
|
||||
LOG_DEBUG("Got %d files in manifest", filesManifest.size());
|
||||
|
||||
|
||||
@@ -4,17 +4,19 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
uint32_t getPositionPrecisionForChannel(uint8_t channelIndex)
|
||||
uint32_t getPositionPrecisionForChannel(const meshtastic_Channel &channel)
|
||||
{
|
||||
const meshtastic_Channel &channel = channels.getByIndex(channelIndex);
|
||||
|
||||
if (channel.settings.has_module_settings) {
|
||||
return channel.settings.module_settings.position_precision;
|
||||
} else if (channel.role == meshtastic_Channel_Role_PRIMARY) {
|
||||
return 32;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
// No module settings: fail closed. A PRIMARY channel used to default to 32
|
||||
// here, leaking an exact position on a sharing-disabled channel. See #10509.
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t getPositionPrecisionForChannel(uint8_t channelIndex)
|
||||
{
|
||||
return getPositionPrecisionForChannel(channels.getByIndex(channelIndex));
|
||||
}
|
||||
|
||||
static int32_t truncateCoordinate(int32_t coordinate, uint32_t precision)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "meshtastic/channel.pb.h"
|
||||
#include "meshtastic/mesh.pb.h"
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t getPositionPrecisionForChannel(const meshtastic_Channel &channel);
|
||||
uint32_t getPositionPrecisionForChannel(uint8_t channelIndex);
|
||||
void applyPositionPrecision(meshtastic_Position &position, uint32_t precision);
|
||||
bool applyPositionPrecision(meshtastic_MeshPacket &packet, uint32_t precision);
|
||||
|
||||
@@ -341,4 +341,10 @@ bool RF95Interface::sleep()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int16_t RF95Interface::getCurrentRSSI()
|
||||
{
|
||||
float rssi = lora->getRSSI(false);
|
||||
return (int16_t)round(rssi);
|
||||
}
|
||||
#endif
|
||||
@@ -37,6 +37,8 @@ class RF95Interface : public RadioLibInterface
|
||||
*/
|
||||
virtual void disableInterrupt() override;
|
||||
|
||||
int16_t getCurrentRSSI() override;
|
||||
|
||||
/**
|
||||
* Enable a particular ISR callback glue function
|
||||
*/
|
||||
|
||||
+104
-60
@@ -35,31 +35,33 @@
|
||||
#endif
|
||||
|
||||
static const meshtastic_Config_LoRaConfig_ModemPreset PRESETS_STD[] = {
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST, meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW,
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST,
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST,
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO,
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_LONG_TURBO, MODEM_PRESET_END};
|
||||
PRESET(LONG_FAST), PRESET(LONG_SLOW), PRESET(MEDIUM_SLOW), PRESET(MEDIUM_FAST), PRESET(SHORT_SLOW),
|
||||
PRESET(SHORT_FAST), PRESET(LONG_MODERATE), PRESET(SHORT_TURBO), PRESET(LONG_TURBO), MODEM_PRESET_END};
|
||||
|
||||
static const meshtastic_Config_LoRaConfig_ModemPreset PRESETS_EU_868[] = {
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST, meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW,
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW, meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST,
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW, meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST,
|
||||
meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE, MODEM_PRESET_END};
|
||||
PRESET(LONG_FAST), PRESET(LONG_SLOW), PRESET(MEDIUM_SLOW), PRESET(MEDIUM_FAST),
|
||||
PRESET(SHORT_SLOW), PRESET(SHORT_FAST), PRESET(LONG_MODERATE), MODEM_PRESET_END};
|
||||
|
||||
static const meshtastic_Config_LoRaConfig_ModemPreset PRESETS_UNDEF[] = {meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST,
|
||||
MODEM_PRESET_END};
|
||||
static const meshtastic_Config_LoRaConfig_ModemPreset PRESETS_UNDEF[] = {PRESET(LONG_FAST), MODEM_PRESET_END};
|
||||
|
||||
static const meshtastic_Config_LoRaConfig_ModemPreset PRESETS_LITE[] = {PRESET(LITE_FAST), PRESET(LITE_SLOW), MODEM_PRESET_END};
|
||||
|
||||
static const meshtastic_Config_LoRaConfig_ModemPreset PRESETS_NARROW[] = {PRESET(NARROW_FAST), PRESET(NARROW_SLOW),
|
||||
MODEM_PRESET_END};
|
||||
|
||||
// Region profiles: bundle preset list + regulatory parameters shared across regions
|
||||
// presets, spacing, padding, audio, licensed, text throttle, position throttle, telemetry throttle, override slot
|
||||
const RegionProfile PROFILE_STD = {PRESETS_STD, 0, 0, true, false, 0, 0, 0, 0};
|
||||
const RegionProfile PROFILE_EU868 = {PRESETS_EU_868, 0, 0, false, false, 0, 0, 0, 0};
|
||||
const RegionProfile PROFILE_UNDEF = {PRESETS_UNDEF, 0, 0, true, false, 0, 0, 0, 0};
|
||||
// presets, spacing, padding, audio, licensed, text throttle, position throttle, telemetry throttle
|
||||
const RegionProfile PROFILE_STD = {PRESETS_STD, 0, 0, true, false, 0, 1, 1};
|
||||
const RegionProfile PROFILE_EU868 = {PRESETS_EU_868, 0, 0, false, false, 0, 1, 1};
|
||||
const RegionProfile PROFILE_UNDEF = {PRESETS_UNDEF, 0, 0, true, false, 0, 1, 1};
|
||||
const RegionProfile PROFILE_LITE = {PRESETS_LITE, 0.4, 0.0375f, false, false, 0, 10, 10};
|
||||
const RegionProfile PROFILE_NARROW = {PRESETS_NARROW, 0, 0.0104f, true, false, 0, 1, 1};
|
||||
|
||||
#define RDEF(name, freq_start, freq_end, duty_cycle, power_limit, frequency_switching, wide_lora, profile_ptr) \
|
||||
#define RDEF(name, freq_start, freq_end, duty_cycle, power_limit, frequency_switching, wide_lora, profile_ptr, default_preset, \
|
||||
override_slot) \
|
||||
{ \
|
||||
meshtastic_Config_LoRaConfig_RegionCode_##name, freq_start, freq_end, duty_cycle, power_limit, frequency_switching, \
|
||||
wide_lora, &profile_ptr, #name \
|
||||
wide_lora, &profile_ptr, default_preset, override_slot, #name \
|
||||
}
|
||||
|
||||
const RegionInfo regions[] = {
|
||||
@@ -67,7 +69,7 @@ const RegionInfo regions[] = {
|
||||
https://link.springer.com/content/pdf/bbm%3A978-1-4842-4357-2%2F1.pdf
|
||||
https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/
|
||||
*/
|
||||
RDEF(US, 902.0f, 928.0f, 100, 30, false, false, PROFILE_STD),
|
||||
RDEF(US, 902.0f, 928.0f, 100, 30, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
EN300220 ETSI V3.2.1 [Table B.1, Item H, p. 21]
|
||||
@@ -75,7 +77,7 @@ const RegionInfo regions[] = {
|
||||
https://www.etsi.org/deliver/etsi_en/300200_300299/30022002/03.02.01_60/en_30022002v030201p.pdf
|
||||
FIXME: https://github.com/meshtastic/firmware/issues/3371
|
||||
*/
|
||||
RDEF(EU_433, 433.0f, 434.0f, 10, 10, false, false, PROFILE_STD),
|
||||
RDEF(EU_433, 433.0f, 434.0f, 10, 10, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
/*
|
||||
https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/
|
||||
https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/
|
||||
@@ -90,33 +92,33 @@ const RegionInfo regions[] = {
|
||||
AFA) to avoid a duty cycle. (Please refer to line P page 22 of this document.)
|
||||
https://www.etsi.org/deliver/etsi_en/300200_300299/30022002/03.01.01_60/en_30022002v030101p.pdf
|
||||
*/
|
||||
RDEF(EU_868, 869.4f, 869.65f, 10, 27, false, false, PROFILE_EU868),
|
||||
RDEF(EU_868, 869.4f, 869.65f, 10, 27, false, false, PROFILE_EU868, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
|
||||
*/
|
||||
RDEF(CN, 470.0f, 510.0f, 100, 19, false, false, PROFILE_STD),
|
||||
RDEF(CN, 470.0f, 510.0f, 100, 19, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
|
||||
https://www.arib.or.jp/english/html/overview/doc/5-STD-T108v1_5-E1.pdf
|
||||
https://qiita.com/ammo0613/items/d952154f1195b64dc29f
|
||||
*/
|
||||
RDEF(JP, 920.5f, 923.5f, 100, 13, false, false, PROFILE_STD),
|
||||
RDEF(JP, 920.5f, 923.5f, 100, 13, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
https://www.iot.org.au/wp/wp-content/uploads/2016/12/IoTSpectrumFactSheet.pdf
|
||||
https://iotalliance.org.nz/wp-content/uploads/sites/4/2019/05/IoT-Spectrum-in-NZ-Briefing-Paper.pdf
|
||||
Also used in Brazil.
|
||||
*/
|
||||
RDEF(ANZ, 915.0f, 928.0f, 100, 30, false, false, PROFILE_STD),
|
||||
RDEF(ANZ, 915.0f, 928.0f, 100, 30, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
433.05 - 434.79 MHz, 25mW EIRP max, No duty cycle restrictions
|
||||
AU Low Interference Potential https://www.acma.gov.au/licences/low-interference-potential-devices-lipd-class-licence
|
||||
NZ General User Radio Licence for Short Range Devices https://gazette.govt.nz/notice/id/2022-go3100
|
||||
*/
|
||||
RDEF(ANZ_433, 433.05f, 434.79f, 100, 14, false, false, PROFILE_STD),
|
||||
RDEF(ANZ_433, 433.05f, 434.79f, 100, 14, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
https://digital.gov.ru/uploaded/files/prilozhenie-12-k-reshenyu-gkrch-18-46-03-1.pdf
|
||||
@@ -124,13 +126,13 @@ const RegionInfo regions[] = {
|
||||
Note:
|
||||
- We do LBT, so 100% is allowed.
|
||||
*/
|
||||
RDEF(RU, 868.7f, 869.2f, 100, 20, false, false, PROFILE_STD),
|
||||
RDEF(RU, 868.7f, 869.2f, 100, 20, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
https://www.law.go.kr/LSW/admRulLsInfoP.do?admRulId=53943&efYd=0
|
||||
https://resources.lora-alliance.org/technical-specifications/rp002-1-0-4-regional-parameters
|
||||
*/
|
||||
RDEF(KR, 920.0f, 923.0f, 100, 23, false, false, PROFILE_STD),
|
||||
RDEF(KR, 920.0f, 923.0f, 100, 23, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
Taiwan, 920-925Mhz, limited to 0.5W indoor or coastal, 1.0W outdoor.
|
||||
@@ -138,40 +140,40 @@ const RegionInfo regions[] = {
|
||||
https://www.ncc.gov.tw/english/files/23070/102_5190_230703_1_doc_C.PDF
|
||||
https://gazette.nat.gov.tw/egFront/e_detail.do?metaid=147283
|
||||
*/
|
||||
RDEF(TW, 920.0f, 925.0f, 100, 27, false, false, PROFILE_STD),
|
||||
RDEF(TW, 920.0f, 925.0f, 100, 27, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
|
||||
*/
|
||||
RDEF(IN, 865.0f, 867.0f, 100, 30, false, false, PROFILE_STD),
|
||||
RDEF(IN, 865.0f, 867.0f, 100, 30, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
https://rrf.rsm.govt.nz/smart-web/smart/page/-smart/domain/licence/LicenceSummary.wdk?id=219752
|
||||
https://iotalliance.org.nz/wp-content/uploads/sites/4/2019/05/IoT-Spectrum-in-NZ-Briefing-Paper.pdf
|
||||
*/
|
||||
RDEF(NZ_865, 864.0f, 868.0f, 100, 36, false, false, PROFILE_STD),
|
||||
RDEF(NZ_865, 864.0f, 868.0f, 100, 36, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
|
||||
https://standard.nbtc.go.th/getattachment/Standards/%E0%B8%A1%E0%B8%B2%E0%B8%95%E0%B8%A3%E0%B8%90%E0%B8%B2%E0%B8%99%E0%B8%97%E0%B8%B2%E0%B8%87%E0%B9%80%E0%B8%97%E0%B8%84%E0%B8%99%E0%B8%B4%E0%B8%84%E0%B8%82%E0%B8%AD%E0%B8%87%E0%B9%80%E0%B8%84%E0%B8%A3%E0%B8%B7%E0%B9%88%E0%B8%AD%E0%B8%87%E0%B9%82%E0%B8%97%E0%B8%A3%E0%B8%84%E0%B8%A1%E0%B8%99%E0%B8%B2%E0%B8%84%E0%B8%A1/1033-2565.pdf.aspx?lang=th-TH
|
||||
Thailand 920–925 MHz set max TX power to 27 dBm and enforce 10% duty cycle, aligned with NBTC regulations.
|
||||
*/
|
||||
RDEF(TH, 920.0f, 925.0f, 10, 27, false, false, PROFILE_STD),
|
||||
RDEF(TH, 920.0f, 925.0f, 10, 27, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
433,05-434,7 Mhz 10 mW
|
||||
868,0-868,6 Mhz 25 mW
|
||||
https://nkrzi.gov.ua/images/upload/256/5810/PDF_UUZ_19_01_2016.pdf
|
||||
*/
|
||||
RDEF(UA_433, 433.0f, 434.7f, 10, 10, false, false, PROFILE_STD),
|
||||
RDEF(UA_868, 868.0f, 868.6f, 1, 14, false, false, PROFILE_STD),
|
||||
RDEF(UA_433, 433.0f, 434.7f, 10, 10, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
RDEF(UA_868, 868.0f, 868.6f, 1, 14, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
Malaysia
|
||||
433 - 435 MHz at 100mW, no restrictions.
|
||||
https://www.mcmc.gov.my/skmmgovmy/media/General/pdf/Short-Range-Devices-Specification.pdf
|
||||
*/
|
||||
RDEF(MY_433, 433.0f, 435.0f, 100, 20, false, false, PROFILE_STD),
|
||||
RDEF(MY_433, 433.0f, 435.0f, 100, 20, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
Malaysia
|
||||
@@ -180,14 +182,14 @@ const RegionInfo regions[] = {
|
||||
Frequency hopping is used for 919 - 923 MHz.
|
||||
https://www.mcmc.gov.my/skmmgovmy/media/General/pdf/Short-Range-Devices-Specification.pdf
|
||||
*/
|
||||
RDEF(MY_919, 919.0f, 924.0f, 100, 27, true, false, PROFILE_STD),
|
||||
RDEF(MY_919, 919.0f, 924.0f, 100, 27, true, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
Singapore
|
||||
SG_923 Band 30d: 917 - 925 MHz at 100mW, no restrictions.
|
||||
https://www.imda.gov.sg/-/media/imda/files/regulation-licensing-and-consultations/ict-standards/telecommunication-standards/radio-comms/imdatssrd.pdf
|
||||
*/
|
||||
RDEF(SG_923, 917.0f, 925.0f, 100, 20, false, false, PROFILE_STD),
|
||||
RDEF(SG_923, 917.0f, 925.0f, 100, 20, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
Philippines
|
||||
@@ -197,9 +199,9 @@ const RegionInfo regions[] = {
|
||||
https://github.com/meshtastic/firmware/issues/4948#issuecomment-2394926135
|
||||
*/
|
||||
|
||||
RDEF(PH_433, 433.0f, 434.7f, 100, 10, false, false, PROFILE_STD),
|
||||
RDEF(PH_868, 868.0f, 869.4f, 100, 14, false, false, PROFILE_STD),
|
||||
RDEF(PH_915, 915.0f, 918.0f, 100, 24, false, false, PROFILE_STD),
|
||||
RDEF(PH_433, 433.0f, 434.7f, 100, 10, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
RDEF(PH_868, 868.0f, 869.4f, 100, 14, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
RDEF(PH_915, 915.0f, 918.0f, 100, 24, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
Kazakhstan
|
||||
@@ -207,32 +209,46 @@ const RegionInfo regions[] = {
|
||||
863 - 868 MHz <25 mW EIRP, 500kHz channels allowed, must not be used at airfields
|
||||
https://github.com/meshtastic/firmware/issues/7204
|
||||
*/
|
||||
RDEF(KZ_433, 433.075f, 434.775f, 100, 10, false, false, PROFILE_STD),
|
||||
RDEF(KZ_863, 863.0f, 868.0f, 100, 30, false, false, PROFILE_STD),
|
||||
RDEF(KZ_433, 433.075f, 434.775f, 100, 10, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
RDEF(KZ_863, 863.0f, 868.0f, 100, 30, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
Nepal
|
||||
865 MHz to 868 MHz frequency band for IoT (Internet of Things), M2M (Machine-to-Machine), and smart metering use,
|
||||
specifically in non-cellular mode. https://www.nta.gov.np/uploads/contents/Radio-Frequency-Policy-2080-English.pdf
|
||||
*/
|
||||
RDEF(NP_865, 865.0f, 868.0f, 100, 30, false, false, PROFILE_STD),
|
||||
RDEF(NP_865, 865.0f, 868.0f, 100, 30, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
Brazil
|
||||
902 - 907.5 MHz , 1W power limit, no duty cycle restrictions
|
||||
https://github.com/meshtastic/firmware/issues/3741
|
||||
*/
|
||||
RDEF(BR_902, 902.0f, 907.5f, 100, 30, false, false, PROFILE_STD),
|
||||
RDEF(BR_902, 902.0f, 907.5f, 100, 30, false, false, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
2.4 GHZ WLAN Band equivalent. Only for SX128x chips.
|
||||
*/
|
||||
RDEF(LORA_24, 2400.0f, 2483.5f, 100, 10, false, true, PROFILE_STD),
|
||||
RDEF(LORA_24, 2400.0f, 2483.5f, 100, 10, false, true, PROFILE_STD, PRESET(LONG_FAST), 0),
|
||||
|
||||
/*
|
||||
EU 866MHz band (Band no. 46b of 2006/771/EC and subsequent amendments) for Non-specific short-range devices (SRD)
|
||||
Gives 4 channels at 865.7/866.3/866.9/867.5 MHz, 400 kHz gap plus 37.5 kHz padding between channels, 27 dBm,
|
||||
duty cycle 2.5% (mobile) or 10% (fixed) https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:02006D0771(01)-20250123
|
||||
*/
|
||||
RDEF(EU_866, 865.6f, 867.6f, 2.5, 27, false, false, PROFILE_LITE, PRESET(LITE_FAST), 0),
|
||||
|
||||
/*
|
||||
EU 868MHz band: 3 channels at 869.410/869.4625/869.577 MHz
|
||||
Channel centres at 869.442/869.525/869.608 MHz,
|
||||
10.4 kHz padding on channels, 27 dBm, duty cycle 10%
|
||||
*/
|
||||
RDEF(EU_N_868, 869.4f, 869.65f, 10, 27, false, false, PROFILE_NARROW, PRESET(NARROW_SLOW), 1),
|
||||
|
||||
/*
|
||||
This needs to be last. Same as US.
|
||||
*/
|
||||
RDEF(UNSET, 902.0f, 928.0f, 100, 30, false, false, PROFILE_UNDEF)
|
||||
RDEF(UNSET, 902.0f, 928.0f, 100, 30, false, false, PROFILE_UNDEF, PRESET(LONG_FAST), 0),
|
||||
|
||||
};
|
||||
|
||||
@@ -546,6 +562,23 @@ const RegionInfo *getRegion(meshtastic_Config_LoRaConfig_RegionCode code)
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get duty cycle for current region. EU_866: 10% for routers, 2.5% for mobile.
|
||||
*/
|
||||
float getEffectiveDutyCycle()
|
||||
{
|
||||
if (myRegion->code == meshtastic_Config_LoRaConfig_RegionCode_EU_866) {
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||
config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE) {
|
||||
return 10.0f;
|
||||
} else {
|
||||
return 2.5f;
|
||||
}
|
||||
}
|
||||
// For all other regions, return the standard duty cycle
|
||||
return myRegion->dutyCycle;
|
||||
}
|
||||
|
||||
uint32_t RadioInterface::getPacketTime(const meshtastic_MeshPacket *p, bool received)
|
||||
{
|
||||
uint32_t pl = 0;
|
||||
@@ -897,12 +930,15 @@ bool RadioInterface::checkOrClampConfigLora(meshtastic_Config_LoRaConfig &loraCo
|
||||
if (loraConfig.override_frequency == 0) {
|
||||
|
||||
// Check if we use the default frequency slot
|
||||
// overrideSlot: 0 = channel hash, -1 = preset hash, >0 = explicit slot
|
||||
uses_default_frequency_slot =
|
||||
(loraConfig.channel_num == 0) || // user choice unset, no frequency override, so use default
|
||||
(newRegion->profile->overrideSlot != 0 &&
|
||||
loraConfig.channel_num == newRegion->profile->overrideSlot) || // user setting matches override
|
||||
((newRegion->profile->overrideSlot == 0) &&
|
||||
((uint32_t)(loraConfig.channel_num - 1) == presetNameHashSlot)); // user setting matches preset hash, no override
|
||||
(newRegion->overrideSlot > 0 &&
|
||||
loraConfig.channel_num == newRegion->overrideSlot) || // user setting matches explicit override slot
|
||||
((newRegion->overrideSlot == OVERRIDE_SLOT_DEFAULT_CHANNEL_HASH) &&
|
||||
((uint32_t)(loraConfig.channel_num - 1) == channelNameHashSlot)) || // user setting matches channel name hash
|
||||
((newRegion->overrideSlot == OVERRIDE_SLOT_PRESET_HASH) &&
|
||||
((uint32_t)(loraConfig.channel_num - 1) == presetNameHashSlot)); // user setting matches preset name hash
|
||||
|
||||
// check if user setting different to preset name
|
||||
uses_custom_channel_name = (strcmp(channelName, presetNameDisplay) != 0);
|
||||
@@ -917,10 +953,13 @@ bool RadioInterface::checkOrClampConfigLora(meshtastic_Config_LoRaConfig &loraCo
|
||||
if (clamp) {
|
||||
if (uses_custom_channel_name) { // clamp to channel name hash
|
||||
loraConfig.channel_num =
|
||||
channelNameHashSlot + 1; // channel_num is 1-based, but hash slot is 0-based, so add 1
|
||||
} else if ((loraConfig.use_preset) && (newRegion->profile->overrideSlot != 0)) { // clamp to preset override slot
|
||||
loraConfig.channel_num =
|
||||
newRegion->profile->overrideSlot; // use the override slot specified by the region profile
|
||||
channelNameHashSlot + 1; // channel_num is 1-based, but hash slot is 0-based, so add 1
|
||||
} else if (newRegion->overrideSlot > 0) { // clamp to explicit override slot
|
||||
loraConfig.channel_num = newRegion->overrideSlot; // use the explicit override slot defined for this region
|
||||
uses_default_frequency_slot = true;
|
||||
} else if (newRegion->overrideSlot == OVERRIDE_SLOT_PRESET_HASH && loraConfig.use_preset) {
|
||||
// clamp to preset name hash
|
||||
loraConfig.channel_num = presetNameHashSlot + 1; // channel_num is 1-based, but hash slot is 0-based, so add 1
|
||||
uses_default_frequency_slot = true;
|
||||
} else if (loraConfig.use_preset) { // clamp to preset slot
|
||||
loraConfig.channel_num = presetNameHashSlot + 1; // channel_num is 1-based, but hash slot is 0-based, so add 1
|
||||
@@ -1018,6 +1057,8 @@ void RadioInterface::applyModemConfig()
|
||||
// Calculate hash of channel name and preset name to pick a default frequency slot if user has not specified one.
|
||||
// Note that channel_num is actually (channel_num - 1), i.e. zero-based, since modulus (%) returns values from 0 to
|
||||
// (numFreqSlots - 1).
|
||||
const char *channelName = channels.getName(channels.getPrimaryIndex());
|
||||
uint32_t channelNameHashSlot = hash(channelName) % numFreqSlots;
|
||||
uint32_t presetNameHashSlot =
|
||||
hash(DisplayFormatters::getModemPresetDisplayName(loraConfig.modem_preset, false, loraConfig.use_preset)) % numFreqSlots;
|
||||
|
||||
@@ -1034,11 +1075,13 @@ void RadioInterface::applyModemConfig()
|
||||
// (channel_num - 1), i.e. zero-based, since modulus (%) returns values from 0 to (numFreqSlots - 1).
|
||||
// NB: channel_num is also know as frequency slot but it's too late to fix now.
|
||||
if (uses_default_frequency_slot) {
|
||||
// if there's an override slot, use that
|
||||
if (newRegion->profile->overrideSlot != 0) {
|
||||
channel_num = newRegion->profile->overrideSlot - 1;
|
||||
// Handle three override slot cases: explicit slot (>0), preset hash (-1), or channel hash (0)
|
||||
if (newRegion->overrideSlot > 0) {
|
||||
channel_num = newRegion->overrideSlot - 1; // explicit override slot (1-based to 0-based)
|
||||
} else if (newRegion->overrideSlot == OVERRIDE_SLOT_PRESET_HASH) {
|
||||
channel_num = presetNameHashSlot; // use preset name hash
|
||||
} else {
|
||||
channel_num = presetNameHashSlot;
|
||||
channel_num = channelNameHashSlot; // use channel name hash (default case)
|
||||
}
|
||||
} else { // use the manually defined one
|
||||
channel_num = loraConfig.channel_num - 1;
|
||||
@@ -1051,7 +1094,6 @@ void RadioInterface::applyModemConfig()
|
||||
|
||||
saveChannelNum(channel_num);
|
||||
saveFreq(freq + loraConfig.frequency_offset);
|
||||
const char *channelName = channels.getName(channels.getPrimaryIndex());
|
||||
|
||||
if (newRegion->wideLora) { // clamp if wide freq range
|
||||
preambleLength = wideLoraPreambleLengthDefault; // 12 is the default for operation above 2GHz
|
||||
@@ -1068,9 +1110,11 @@ void RadioInterface::applyModemConfig()
|
||||
channel_num, power);
|
||||
LOG_INFO("newRegion->freqStart -> newRegion->freqEnd: %f -> %f (%f MHz)", newRegion->freqStart, newRegion->freqEnd,
|
||||
newRegion->freqEnd - newRegion->freqStart);
|
||||
LOG_INFO("numFreqSlots: %d x %.3fkHz", numFreqSlots, bw);
|
||||
if (newRegion->profile->overrideSlot != 0) {
|
||||
LOG_INFO("Using region override slot: %d", newRegion->profile->overrideSlot);
|
||||
LOG_INFO("numFreqSlots: %u x %.3fkHz", numFreqSlots, bw);
|
||||
if (newRegion->overrideSlot > 0) {
|
||||
LOG_INFO("Using region explicit override slot: %d", newRegion->overrideSlot);
|
||||
} else if (newRegion->overrideSlot == OVERRIDE_SLOT_PRESET_HASH) {
|
||||
LOG_INFO("Using region preset name hash for slot selection");
|
||||
}
|
||||
LOG_INFO("channel_num: %d", channel_num + 1);
|
||||
LOG_INFO("frequency: %f", getFreq());
|
||||
|
||||
@@ -283,6 +283,12 @@ class RadioInterface
|
||||
*/
|
||||
virtual void saveChannelNum(uint32_t savedChannelNum);
|
||||
|
||||
/**
|
||||
* Get current RSSI reading from the radio.
|
||||
* Returns 0 if not available.
|
||||
*/
|
||||
virtual int16_t getCurrentRSSI() { return 0; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Convert our modemConfig enum into wf, sf, etc...
|
||||
|
||||
+107
-15
@@ -15,6 +15,7 @@
|
||||
#include "PortduinoGlue.h"
|
||||
#include "meshUtils.h"
|
||||
#endif
|
||||
|
||||
void LockingArduinoHal::spiBeginTransaction()
|
||||
{
|
||||
spiLock->lock();
|
||||
@@ -28,6 +29,7 @@ void LockingArduinoHal::spiEndTransaction()
|
||||
|
||||
spiLock->unlock();
|
||||
}
|
||||
|
||||
#if ARCH_PORTDUINO
|
||||
void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in)
|
||||
{
|
||||
@@ -40,22 +42,18 @@ RadioLibInterface::RadioLibInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE c
|
||||
: NotifiedWorkerThread("RadioIf"), module(hal, cs, irq, rst, busy), iface(_iface)
|
||||
{
|
||||
instance = this;
|
||||
|
||||
// Initialize unused sample slots to a sane default; sample count controls averaging.
|
||||
for (uint8_t i = 0; i < NOISE_FLOOR_SAMPLES; i++) {
|
||||
noiseFloorSamples[i] = NOISE_FLOOR_DEFAULT;
|
||||
}
|
||||
|
||||
#if defined(ARCH_STM32WL) && defined(USE_SX1262)
|
||||
module.setCb_digitalWrite(stm32wl_emulate_digitalWrite);
|
||||
module.setCb_digitalRead(stm32wl_emulate_digitalRead);
|
||||
#endif
|
||||
}
|
||||
|
||||
RadioLibInterface::~RadioLibInterface()
|
||||
{
|
||||
// If the static `instance` pointer still references us, clear it.
|
||||
// A later successful init() may have replaced `instance` with a newer
|
||||
// interface — don't clobber that case.
|
||||
if (instance == this) {
|
||||
instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
// ESP32 doesn't use that flag
|
||||
#define YIELD_FROM_ISR(x) portYIELD_FROM_ISR()
|
||||
@@ -256,6 +254,87 @@ bool RadioLibInterface::findInTxQueue(NodeNum from, PacketId id)
|
||||
return txQueue.find(from, id);
|
||||
}
|
||||
|
||||
void RadioLibInterface::updateNoiseFloor()
|
||||
{
|
||||
// Only sample from idle receive mode. TX/RX-critical paths must return to radio work quickly.
|
||||
if (!isReceiving || sendingPacket != NULL || isActivelyReceiving() || isIRQPending()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t now = millis();
|
||||
if (now - lastNoiseFloorUpdate < NOISE_FLOOR_UPDATE_INTERVAL_MS) {
|
||||
return;
|
||||
}
|
||||
lastNoiseFloorUpdate = now;
|
||||
|
||||
int16_t rssi = getCurrentRSSI();
|
||||
if (rssi == NOISE_FLOOR_INVALID || rssi >= 0 || rssi < NOISE_FLOOR_VALID_MIN) {
|
||||
LOG_DEBUG("Skipping invalid RSSI reading: %d", rssi);
|
||||
return;
|
||||
}
|
||||
|
||||
noiseFloorSamples[currentSampleIndex] = (int32_t)rssi;
|
||||
currentSampleIndex++;
|
||||
|
||||
if (currentSampleIndex >= NOISE_FLOOR_SAMPLES) {
|
||||
currentSampleIndex = 0;
|
||||
isNoiseFloorBufferFull = true;
|
||||
}
|
||||
|
||||
currentNoiseFloor = getAverageNoiseFloorInternal();
|
||||
|
||||
LOG_DEBUG("Noise floor: %d dBm (samples: %d, latest: %d dBm)", currentNoiseFloor, getNoiseFloorSampleCountInternal(), rssi);
|
||||
}
|
||||
|
||||
uint8_t RadioLibInterface::getNoiseFloorSampleCountInternal() const
|
||||
{
|
||||
return isNoiseFloorBufferFull ? NOISE_FLOOR_SAMPLES : currentSampleIndex;
|
||||
}
|
||||
|
||||
int32_t RadioLibInterface::getAverageNoiseFloorInternal() const
|
||||
{
|
||||
uint8_t sampleCount = getNoiseFloorSampleCountInternal();
|
||||
|
||||
if (sampleCount == 0) {
|
||||
return NOISE_FLOOR_DEFAULT;
|
||||
}
|
||||
|
||||
int32_t sum = 0;
|
||||
for (uint8_t i = 0; i < sampleCount; i++) {
|
||||
sum += noiseFloorSamples[i];
|
||||
}
|
||||
|
||||
return sum / sampleCount;
|
||||
}
|
||||
|
||||
int32_t RadioLibInterface::getAverageNoiseFloor()
|
||||
{
|
||||
return getAverageNoiseFloorInternal();
|
||||
}
|
||||
|
||||
int32_t RadioLibInterface::getNoiseFloor()
|
||||
{
|
||||
return currentNoiseFloor;
|
||||
}
|
||||
|
||||
bool RadioLibInterface::hasNoiseFloorSamples()
|
||||
{
|
||||
return getNoiseFloorSampleCountInternal() > 0;
|
||||
}
|
||||
|
||||
uint8_t RadioLibInterface::getNoiseFloorSampleCount()
|
||||
{
|
||||
return getNoiseFloorSampleCountInternal();
|
||||
}
|
||||
|
||||
void RadioLibInterface::resetNoiseFloor()
|
||||
{
|
||||
currentSampleIndex = 0;
|
||||
isNoiseFloorBufferFull = false;
|
||||
currentNoiseFloor = NOISE_FLOOR_DEFAULT;
|
||||
LOG_INFO("Noise floor reset - rolling window collection will restart");
|
||||
}
|
||||
|
||||
bool RadioLibInterface::randomBytes(uint8_t *buffer, size_t length)
|
||||
{
|
||||
if (!buffer || length == 0 || !iface) {
|
||||
@@ -283,6 +362,7 @@ currently active.
|
||||
*/
|
||||
void RadioLibInterface::onNotify(uint32_t notification)
|
||||
{
|
||||
|
||||
switch (notification) {
|
||||
case ISR_TX:
|
||||
handleTransmitInterrupt();
|
||||
@@ -414,11 +494,6 @@ bool RadioLibInterface::removePendingTXPacket(NodeNum from, PacketId id, uint32_
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a packet that is eligible for replacement from the TX queue
|
||||
*/
|
||||
// void RadioLibInterface::removePending
|
||||
|
||||
void RadioLibInterface::handleTransmitInterrupt()
|
||||
{
|
||||
// This can be null if we forced the device to enter standby mode. In that case
|
||||
@@ -434,6 +509,9 @@ void RadioLibInterface::completeSending()
|
||||
// that can take a long time
|
||||
auto p = sendingPacket;
|
||||
sendingPacket = NULL;
|
||||
#ifdef LED_LORA
|
||||
digitalWrite(LED_LORA, LED_STATE_OFF);
|
||||
#endif
|
||||
|
||||
if (p) {
|
||||
// Packet has been sent, count it toward our TX airtime utilization.
|
||||
@@ -555,6 +633,9 @@ void RadioLibInterface::pollMissedIrqs()
|
||||
if (isReceiving) {
|
||||
checkRxDoneIrqFlag();
|
||||
}
|
||||
if (sendingPacket) {
|
||||
checkTxDoneIrqFlag();
|
||||
}
|
||||
}
|
||||
|
||||
void RadioLibInterface::resetAGC()
|
||||
@@ -570,6 +651,14 @@ void RadioLibInterface::checkRxDoneIrqFlag()
|
||||
}
|
||||
}
|
||||
|
||||
void RadioLibInterface::checkTxDoneIrqFlag()
|
||||
{
|
||||
if (iface->checkIrq(RADIOLIB_IRQ_TX_DONE)) {
|
||||
LOG_WARN("caught missed TX_DONE");
|
||||
notify(ISR_TX, true);
|
||||
}
|
||||
}
|
||||
|
||||
void RadioLibInterface::configHardwareForSend()
|
||||
{
|
||||
powerMon->setState(meshtastic_PowerMon_State_Lora_TXOn);
|
||||
@@ -611,6 +700,9 @@ bool RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
|
||||
enableInterrupt(isrTxLevel0);
|
||||
lastTxStart = millis();
|
||||
printPacket("Started Tx", txp);
|
||||
#ifdef LED_LORA
|
||||
digitalWrite(LED_LORA, LED_STATE_ON);
|
||||
#endif
|
||||
}
|
||||
|
||||
return res == RADIOLIB_ERR_NONE;
|
||||
|
||||
@@ -99,11 +99,49 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
/// are _trying_ to receive a packet currently (note - we might just be waiting for one)
|
||||
bool isReceiving = false;
|
||||
|
||||
protected:
|
||||
// Noise floor tracking - rolling window of samples.
|
||||
static const uint8_t NOISE_FLOOR_SAMPLES = 20;
|
||||
static const int32_t NOISE_FLOOR_DEFAULT = -120;
|
||||
static const int32_t NOISE_FLOOR_VALID_MIN = -127;
|
||||
static const int32_t NOISE_FLOOR_INVALID = -128;
|
||||
int32_t noiseFloorSamples[NOISE_FLOOR_SAMPLES];
|
||||
uint8_t currentSampleIndex = 0;
|
||||
bool isNoiseFloorBufferFull = false;
|
||||
uint32_t lastNoiseFloorUpdate = 0;
|
||||
static const uint32_t NOISE_FLOOR_UPDATE_INTERVAL_MS = 5000;
|
||||
int32_t currentNoiseFloor = NOISE_FLOOR_DEFAULT;
|
||||
|
||||
/**
|
||||
* Pure virtual hook for derived radio interfaces to provide instantaneous RSSI.
|
||||
* Implementations should return dBm, or an invalid value that updateNoiseFloor()
|
||||
* can reject.
|
||||
*/
|
||||
virtual int16_t getCurrentRSSI() = 0;
|
||||
|
||||
public:
|
||||
/** Our ISR code currently needs this to find our active instance
|
||||
*/
|
||||
static RadioLibInterface *instance;
|
||||
|
||||
/** Clear instance on destruction so stale pointer checks in loop() are safe */
|
||||
virtual ~RadioLibInterface()
|
||||
{
|
||||
if (instance == this)
|
||||
instance = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current calculated noise floor in dBm
|
||||
* Returns -120 dBm if not yet calibrated
|
||||
*/
|
||||
int32_t getNoiseFloor();
|
||||
|
||||
/**
|
||||
* Calculate the average noise floor from collected samples
|
||||
*/
|
||||
int32_t getAverageNoiseFloor();
|
||||
|
||||
/**
|
||||
* Glue functions called from ISR land
|
||||
*/
|
||||
@@ -136,13 +174,6 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
RadioLibInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||
RADIOLIB_PIN_TYPE busy, PhysicalLayer *iface = NULL);
|
||||
|
||||
/**
|
||||
* Clear the static `instance` pointer if it still points at us, so callers
|
||||
* that check `RadioLibInterface::instance != nullptr` don't dereference a
|
||||
* freed object after a failed init() + unique_ptr reset.
|
||||
*/
|
||||
virtual ~RadioLibInterface();
|
||||
|
||||
virtual ErrorCode send(meshtastic_MeshPacket *p) override;
|
||||
|
||||
/**
|
||||
@@ -179,6 +210,28 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
/** Attempt to find a packet in the TxQueue. Returns true if the packet was found. */
|
||||
virtual bool findInTxQueue(NodeNum from, PacketId id) override;
|
||||
|
||||
/**
|
||||
* Update the noise floor measurement by sampling RSSI from a slow path.
|
||||
* This should not be called from radio interrupt or TX/RX critical paths.
|
||||
*/
|
||||
void updateNoiseFloor();
|
||||
|
||||
/**
|
||||
* Check if we have collected any noise floor samples
|
||||
*/
|
||||
bool hasNoiseFloorSamples();
|
||||
|
||||
/**
|
||||
* Get the number of samples in the rolling window
|
||||
*/
|
||||
uint8_t getNoiseFloorSampleCount();
|
||||
|
||||
/**
|
||||
* Reset the noise floor calibration
|
||||
* Will automatically restart collection
|
||||
*/
|
||||
void resetNoiseFloor();
|
||||
|
||||
/**
|
||||
* Request randomness sourced from the LoRa modem, if supported by the active RadioLib interface.
|
||||
* @return true if len bytes were produced, false otherwise.
|
||||
@@ -186,6 +239,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
bool randomBytes(uint8_t *buffer, size_t length);
|
||||
|
||||
private:
|
||||
uint8_t getNoiseFloorSampleCountInternal() const;
|
||||
int32_t getAverageNoiseFloorInternal() const;
|
||||
|
||||
/** if we have something waiting to send, start a short (random) timer so we can come check for collision before actually
|
||||
* doing the transmit */
|
||||
void setTransmitDelay();
|
||||
@@ -293,4 +349,5 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
bool removePendingTXPacket(NodeNum from, PacketId id, uint32_t hop_limit_lt) override;
|
||||
|
||||
void checkRxDoneIrqFlag();
|
||||
void checkTxDoneIrqFlag();
|
||||
};
|
||||
|
||||
+12
-7
@@ -318,10 +318,11 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
||||
} // should have already been handled by sendLocal
|
||||
|
||||
// Abort sending if we are violating the duty cycle
|
||||
if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100) {
|
||||
float effectiveDutyCycle = getEffectiveDutyCycle();
|
||||
if (!config.lora.override_duty_cycle && effectiveDutyCycle < 100) {
|
||||
float hourlyTxPercent = airTime->utilizationTXPercent();
|
||||
if (hourlyTxPercent > myRegion->dutyCycle) {
|
||||
uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle);
|
||||
if (hourlyTxPercent > effectiveDutyCycle) {
|
||||
uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, effectiveDutyCycle);
|
||||
|
||||
LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d mins", silentMinutes);
|
||||
|
||||
@@ -368,10 +369,14 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
||||
}
|
||||
|
||||
fixPriority(p); // Before encryption, fix the priority if it's unset
|
||||
if (!applyPositionPrecisionForChannel(*p, p->channel)) {
|
||||
LOG_ERROR("Dropping malformed position packet before send");
|
||||
packetPool.release(p);
|
||||
return meshtastic_Routing_Error_BAD_REQUEST;
|
||||
// Position precision is an originator-only privacy policy. Relays keep
|
||||
// p->from as the original sender, so do not rewrite their POSITION_APP payload.
|
||||
if (isFromUs(p)) {
|
||||
if (!applyPositionPrecisionForChannel(*p, p->channel)) {
|
||||
LOG_ERROR("Dropping malformed position packet before send");
|
||||
packetPool.release(p);
|
||||
return meshtastic_Routing_Error_BAD_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
// If the packet is not yet encrypted, do so now
|
||||
|
||||
@@ -52,8 +52,8 @@ template <typename T> bool SX126xInterface<T>::init()
|
||||
|
||||
#ifdef SX126X_POWER_EN // Perhaps add RADIOLIB_NC check, and beforehand define as such if it is undefined, but it is not commonly
|
||||
// used and not part of the 'default' set of pin definitions.
|
||||
digitalWrite(SX126X_POWER_EN, HIGH);
|
||||
pinMode(SX126X_POWER_EN, OUTPUT);
|
||||
digitalWrite(SX126X_POWER_EN, HIGH);
|
||||
#endif
|
||||
|
||||
#if HAS_LORA_FEM
|
||||
@@ -65,8 +65,8 @@ template <typename T> bool SX126xInterface<T>::init()
|
||||
#endif
|
||||
|
||||
#ifdef RF95_FAN_EN
|
||||
digitalWrite(RF95_FAN_EN, HIGH);
|
||||
pinMode(RF95_FAN_EN, OUTPUT);
|
||||
digitalWrite(RF95_FAN_EN, HIGH);
|
||||
#endif
|
||||
|
||||
#if ARCH_PORTDUINO
|
||||
@@ -265,6 +265,12 @@ template <typename T> bool SX126xInterface<T>::reconfigure()
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> int16_t SX126xInterface<T>::getCurrentRSSI()
|
||||
{
|
||||
float rssi = lora.getRSSI(false);
|
||||
return (int16_t)round(rssi);
|
||||
}
|
||||
|
||||
template <typename T> void SX126xInterface<T>::disableInterrupt()
|
||||
{
|
||||
lora.clearDio1Action();
|
||||
|
||||
@@ -41,6 +41,8 @@ template <class T> class SX126xInterface : public RadioLibInterface
|
||||
*/
|
||||
T lora;
|
||||
|
||||
int16_t getCurrentRSSI() override;
|
||||
|
||||
/**
|
||||
* Glue functions called from ISR land
|
||||
*/
|
||||
|
||||
@@ -325,4 +325,10 @@ template <typename T> bool SX128xInterface<T>::sleep()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> int16_t SX128xInterface<T>::getCurrentRSSI()
|
||||
{
|
||||
float rssi = lora.getRSSI(false);
|
||||
return (int16_t)round(rssi);
|
||||
}
|
||||
#endif
|
||||
@@ -35,6 +35,8 @@ template <class T> class SX128xInterface : public RadioLibInterface
|
||||
*/
|
||||
T lora;
|
||||
|
||||
int16_t getCurrentRSSI() override;
|
||||
|
||||
/**
|
||||
* Glue functions called from ISR land
|
||||
*/
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
#include "ServerAPI.h"
|
||||
#include <WiFi.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
|
||||
#include "ServerAPI.h"
|
||||
#if !defined(USE_WS5500) && !defined(USE_CH390D)
|
||||
#if defined(USE_ARDUINO_ETHERNET)
|
||||
#include <Ethernet.h>
|
||||
#else
|
||||
#include <RAK13800_W5100S.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Provides both debug printing and, if the client starts sending protobufs to us, switches to send/receive protobufs
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,347 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Siara Logics (cc)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @author Arundale Ramanathan
|
||||
*
|
||||
* Port for Particle (particle.io) / Aruino - Jonathan Greenblatt
|
||||
*
|
||||
* This file describes each function of the Unishox2 API \n
|
||||
* For finding out how this API can be used in your program, \n
|
||||
* please see test_unishox2.c.
|
||||
*/
|
||||
|
||||
#ifndef unishox2
|
||||
#define unishox2
|
||||
|
||||
#define UNISHOX_VERSION "2.0" ///< Unicode spec version
|
||||
|
||||
/**
|
||||
* Macro switch to enable/disable output buffer length parameter in low level api \n
|
||||
* Disabled by default \n
|
||||
* When this macro is defined, the all the API functions \n
|
||||
* except the simple API functions accept an additional parameter olen \n
|
||||
* that enables the developer to pass the size of the output buffer provided \n
|
||||
* so that the api function may not write beyond that length. \n
|
||||
* This can be disabled if the developer knows that the buffer provided is sufficient enough \n
|
||||
* so no additional parameter is passed and the program is faster since additional check \n
|
||||
* for output length is not performed at each step \n
|
||||
* The simple api, i.e. unishox2_(de)compress_simple will always omit the buffer length
|
||||
*/
|
||||
#ifndef UNISHOX_API_WITH_OUTPUT_LEN
|
||||
#define UNISHOX_API_WITH_OUTPUT_LEN 1
|
||||
#endif
|
||||
|
||||
/// Upto 8 bits of initial magic bit sequence can be included. Bit count can be specified with UNISHOX_MAGIC_BIT_LEN
|
||||
#ifndef UNISHOX_MAGIC_BITS
|
||||
#define UNISHOX_MAGIC_BITS 0xFF
|
||||
#endif
|
||||
|
||||
/// Desired length of Magic bits defined by UNISHOX_MAGIC_BITS
|
||||
#ifdef UNISHOX_MAGIC_BIT_LEN
|
||||
#if UNISHOX_MAGIC_BIT_LEN < 0 || 9 <= UNISHOX_MAGIC_BIT_LEN
|
||||
#error "UNISHOX_MAGIC_BIT_LEN need between [0, 8)"
|
||||
#endif
|
||||
#else
|
||||
#define UNISHOX_MAGIC_BIT_LEN 1
|
||||
#endif
|
||||
|
||||
// enum {USX_ALPHA = 0, USX_SYM, USX_NUM, USX_DICT, USX_DELTA};
|
||||
|
||||
/// Default Horizontal codes. When composition of text is know beforehand, the other hcodes in this section can be used to achieve
|
||||
/// more compression.
|
||||
#define USX_HCODES_DFLT \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
0x00, 0x40, 0x80, 0xC0, 0xE0 \
|
||||
}
|
||||
/// Length of each default hcode
|
||||
#define USX_HCODE_LENS_DFLT \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
2, 2, 2, 3, 3 \
|
||||
}
|
||||
|
||||
/// Horizontal codes preset for English Alphabet content only
|
||||
#define USX_HCODES_ALPHA_ONLY \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00 \
|
||||
}
|
||||
/// Length of each Alpha only hcode
|
||||
#define USX_HCODE_LENS_ALPHA_ONLY \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
0, 0, 0, 0, 0 \
|
||||
}
|
||||
|
||||
/// Horizontal codes preset for Alpha Numeric content only
|
||||
#define USX_HCODES_ALPHA_NUM_ONLY \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
0x00, 0x00, 0x80, 0x00, 0x00 \
|
||||
}
|
||||
/// Length of each Alpha numeric hcode
|
||||
#define USX_HCODE_LENS_ALPHA_NUM_ONLY \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
1, 0, 1, 0, 0 \
|
||||
}
|
||||
|
||||
/// Horizontal codes preset for Alpha Numeric and Symbol content only
|
||||
#define USX_HCODES_ALPHA_NUM_SYM_ONLY \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
0x00, 0x80, 0xC0, 0x00, 0x00 \
|
||||
}
|
||||
/// Length of each Alpha numeric and symbol hcodes
|
||||
#define USX_HCODE_LENS_ALPHA_NUM_SYM_ONLY \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
1, 2, 2, 0, 0 \
|
||||
}
|
||||
|
||||
/// Horizontal codes preset favouring Alphabet content
|
||||
#define USX_HCODES_FAVOR_ALPHA \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
0x00, 0x80, 0xA0, 0xC0, 0xE0 \
|
||||
}
|
||||
/// Length of each hcode favouring Alpha content
|
||||
#define USX_HCODE_LENS_FAVOR_ALPHA \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
1, 3, 3, 3, 3 \
|
||||
}
|
||||
|
||||
/// Horizontal codes preset favouring repeating sequences
|
||||
#define USX_HCODES_FAVOR_DICT \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
0x00, 0x40, 0xC0, 0x80, 0xE0 \
|
||||
}
|
||||
/// Length of each hcode favouring repeating sequences
|
||||
#define USX_HCODE_LENS_FAVOR_DICT \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
2, 2, 3, 2, 3 \
|
||||
}
|
||||
|
||||
/// Horizontal codes preset favouring symbols
|
||||
#define USX_HCODES_FAVOR_SYM \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
0x80, 0x00, 0xA0, 0xC0, 0xE0 \
|
||||
}
|
||||
/// Length of each hcode favouring symbols
|
||||
#define USX_HCODE_LENS_FAVOR_SYM \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
3, 1, 3, 3, 3 \
|
||||
}
|
||||
|
||||
// #define USX_HCODES_FAVOR_UMLAUT {0x00, 0x40, 0xE0, 0xC0, 0x80}
|
||||
// #define USX_HCODE_LENS_FAVOR_UMLAUT {2, 2, 3, 3, 2}
|
||||
|
||||
/// Horizontal codes preset favouring umlaut letters
|
||||
#define USX_HCODES_FAVOR_UMLAUT \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
0x80, 0xA0, 0xC0, 0xE0, 0x00 \
|
||||
}
|
||||
/// Length of each hcode favouring umlaut letters
|
||||
#define USX_HCODE_LENS_FAVOR_UMLAUT \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
3, 3, 3, 3, 1 \
|
||||
}
|
||||
|
||||
/// Horizontal codes preset for no repeating sequences
|
||||
#define USX_HCODES_NO_DICT \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
0x00, 0x40, 0x80, 0x00, 0xC0 \
|
||||
}
|
||||
/// Length of each hcode for no repeating sequences
|
||||
#define USX_HCODE_LENS_NO_DICT \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
2, 2, 2, 0, 2 \
|
||||
}
|
||||
|
||||
/// Horizontal codes preset for no Unicode characters
|
||||
#define USX_HCODES_NO_UNI \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
0x00, 0x40, 0x80, 0xC0, 0x00 \
|
||||
}
|
||||
/// Length of each hcode for no Unicode characters
|
||||
#define USX_HCODE_LENS_NO_UNI \
|
||||
(const unsigned char[]) \
|
||||
{ \
|
||||
2, 2, 2, 2, 0 \
|
||||
}
|
||||
|
||||
extern const char *USX_FREQ_SEQ_DFLT[];
|
||||
extern const char *USX_FREQ_SEQ_TXT[];
|
||||
extern const char *USX_FREQ_SEQ_URL[];
|
||||
extern const char *USX_FREQ_SEQ_JSON[];
|
||||
extern const char *USX_FREQ_SEQ_HTML[];
|
||||
extern const char *USX_FREQ_SEQ_XML[];
|
||||
extern const char *USX_TEMPLATES[];
|
||||
|
||||
/// Default preset parameter set. When composition of text is know beforehand, the other parameter sets in this section can be
|
||||
/// used to achieve more compression.
|
||||
#define USX_PSET_DFLT USX_HCODES_DFLT, USX_HCODE_LENS_DFLT, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
|
||||
/// Preset parameter set for English Alphabet only content
|
||||
#define USX_PSET_ALPHA_ONLY USX_HCODES_ALPHA_ONLY, USX_HCODE_LENS_ALPHA_ONLY, USX_FREQ_SEQ_TXT, USX_TEMPLATES
|
||||
/// Preset parameter set for Alpha numeric content
|
||||
#define USX_PSET_ALPHA_NUM_ONLY USX_HCODES_ALPHA_NUM_ONLY, USX_HCODE_LENS_ALPHA_NUM_ONLY, USX_FREQ_SEQ_TXT, USX_TEMPLATES
|
||||
/// Preset parameter set for Alpha numeric and symbol content
|
||||
#define USX_PSET_ALPHA_NUM_SYM_ONLY \
|
||||
USX_HCODES_ALPHA_NUM_SYM_ONLY, USX_HCODE_LENS_ALPHA_NUM_SYM_ONLY, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
|
||||
/// Preset parameter set for Alpha numeric symbol content having predominantly text
|
||||
#define USX_PSET_ALPHA_NUM_SYM_ONLY_TXT \
|
||||
USX_HCODES_ALPHA_NUM_SYM_ONLY, USX_HCODE_LENS_ALPHA_NUM_SYM_ONLY, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
|
||||
/// Preset parameter set favouring Alphabet content
|
||||
#define USX_PSET_FAVOR_ALPHA USX_HCODES_FAVOR_ALPHA, USX_HCODE_LENS_FAVOR_ALPHA, USX_FREQ_SEQ_TXT, USX_TEMPLATES
|
||||
/// Preset parameter set favouring repeating sequences
|
||||
#define USX_PSET_FAVOR_DICT USX_HCODES_FAVOR_DICT, USX_HCODE_LENS_FAVOR_DICT, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
|
||||
/// Preset parameter set favouring symbols
|
||||
#define USX_PSET_FAVOR_SYM USX_HCODES_FAVOR_SYM, USX_HCODE_LENS_FAVOR_SYM, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
|
||||
/// Preset parameter set favouring unlaut letters
|
||||
#define USX_PSET_FAVOR_UMLAUT USX_HCODES_FAVOR_UMLAUT, USX_HCODE_LENS_FAVOR_UMLAUT, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
|
||||
/// Preset parameter set for when there are no repeating sequences
|
||||
#define USX_PSET_NO_DICT USX_HCODES_NO_DICT, USX_HCODE_LENS_NO_DICT, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
|
||||
/// Preset parameter set for when there are no unicode symbols
|
||||
#define USX_PSET_NO_UNI USX_HCODES_NO_UNI, USX_HCODE_LENS_NO_UNI, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
|
||||
/// Preset parameter set for when there are no unicode symbols favouring text
|
||||
#define USX_PSET_NO_UNI_FAVOR_TEXT USX_HCODES_NO_UNI, USX_HCODE_LENS_NO_UNI, USX_FREQ_SEQ_TXT, USX_TEMPLATES
|
||||
/// Preset parameter set favouring URL content
|
||||
#define USX_PSET_URL USX_HCODES_DFLT, USX_HCODE_LENS_DFLT, USX_FREQ_SEQ_URL, USX_TEMPLATES
|
||||
/// Preset parameter set favouring JSON content
|
||||
#define USX_PSET_JSON USX_HCODES_DFLT, USX_HCODE_LENS_DFLT, USX_FREQ_SEQ_JSON, USX_TEMPLATES
|
||||
/// Preset parameter set favouring JSON content having no Unicode symbols
|
||||
#define USX_PSET_JSON_NO_UNI USX_HCODES_NO_UNI, USX_HCODE_LENS_NO_UNI, USX_FREQ_SEQ_JSON, USX_TEMPLATES
|
||||
/// Preset parameter set favouring XML content
|
||||
#define USX_PSET_XML USX_HCODES_DFLT, USX_HCODE_LENS_DFLT, USX_FREQ_SEQ_XML, USX_TEMPLATES
|
||||
/// Preset parameter set favouring HTML content
|
||||
#define USX_PSET_HTML USX_HCODES_DFLT, USX_HCODE_LENS_DFLT, USX_FREQ_SEQ_HTML, USX_TEMPLATES
|
||||
|
||||
/**
|
||||
* This structure is used when a string array needs to be compressed.
|
||||
* This is passed as a parameter to the unishox2_decompress_lines() function
|
||||
*/
|
||||
struct us_lnk_lst {
|
||||
char *data;
|
||||
struct us_lnk_lst *previous;
|
||||
};
|
||||
|
||||
/**
|
||||
* This macro is for internal use, but builds upon the macro UNISHOX_API_WITH_OUTPUT_LEN
|
||||
* When the macro UNISHOX_API_WITH_OUTPUT_LEN is defined, the all the API functions
|
||||
* except the simple API functions accept an additional parameter olen
|
||||
* that enables the developer to pass the size of the output buffer provided
|
||||
* so that the api function may not write beyond that length.
|
||||
* This can be disabled if the developer knows that the buffer provided is sufficient enough
|
||||
* so no additional parameter is passed and the program is faster since additional check
|
||||
* for output length is not performed at each step
|
||||
*/
|
||||
#if defined(UNISHOX_API_WITH_OUTPUT_LEN) && UNISHOX_API_WITH_OUTPUT_LEN != 0
|
||||
#define UNISHOX_API_OUT_AND_LEN(out, olen) out, olen
|
||||
#else
|
||||
#define UNISHOX_API_OUT_AND_LEN(out, olen) out
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Simple API for compressing a string
|
||||
* @param[in] in Input ASCII / UTF-8 string
|
||||
* @param[in] len length in bytes
|
||||
* @param[out] out output buffer - should be large enough to hold compressed output
|
||||
*/
|
||||
extern int unishox2_compress_simple(const char *in, int len, char *out);
|
||||
/**
|
||||
* Simple API for decompressing a string
|
||||
* @param[in] in Input compressed bytes (output of unishox2_compress functions)
|
||||
* @param[in] len length of 'in' in bytes
|
||||
* @param[out] out output buffer for ASCII / UTF-8 string - should be large enough
|
||||
*/
|
||||
extern int unishox2_decompress_simple(const char *in, int len, char *out);
|
||||
/**
|
||||
* Comprehensive API for compressing a string
|
||||
*
|
||||
* Presets are available for the last four parameters so they can be passed as single parameter. \n
|
||||
* See USX_PSET_* macros. Example call: \n
|
||||
* unishox2_compress(in, len, out, olen, USX_PSET_ALPHA_ONLY);
|
||||
*
|
||||
* @param[in] in Input ASCII / UTF-8 string
|
||||
* @param[in] len length in bytes
|
||||
* @param[out] out output buffer - should be large enough to hold compressed output
|
||||
* @param[in] olen length of 'out' buffer in bytes. Can be omitted if sufficient buffer is provided
|
||||
* @param[in] usx_hcodes Horizontal codes (array of bytes). See macro section for samples.
|
||||
* @param[in] usx_hcode_lens Length of each element in usx_hcodes array
|
||||
* @param[in] usx_freq_seq Frequently occurring sequences. See USX_FREQ_SEQ_* macros for samples
|
||||
* @param[in] usx_templates Templates of frequently occurring patterns. See USX_TEMPLATES macro.
|
||||
*/
|
||||
extern int unishox2_compress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen),
|
||||
const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[], const char *usx_freq_seq[],
|
||||
const char *usx_templates[]);
|
||||
/**
|
||||
* Comprehensive API for de-compressing a string
|
||||
*
|
||||
* Presets are available for the last four parameters so they can be passed as single parameter. \n
|
||||
* See USX_PSET_* macros. Example call: \n
|
||||
* unishox2_decompress(in, len, out, olen, USX_PSET_ALPHA_ONLY);
|
||||
*
|
||||
* @param[in] in Input compressed bytes (output of unishox2_compress functions)
|
||||
* @param[in] len length of 'in' in bytes
|
||||
* @param[out] out output buffer - should be large enough to hold de-compressed output
|
||||
* @param[in] olen length of 'out' buffer in bytes. Can be omitted if sufficient buffer is provided
|
||||
* @param[in] usx_hcodes Horizontal codes (array of bytes). See macro section for samples.
|
||||
* @param[in] usx_hcode_lens Length of each element in usx_hcodes array
|
||||
* @param[in] usx_freq_seq Frequently occurring sequences. See USX_FREQ_SEQ_* macros for samples
|
||||
* @param[in] usx_templates Templates of frequently occurring patterns. See USX_TEMPLATES macro.
|
||||
*/
|
||||
extern int unishox2_decompress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen),
|
||||
const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[], const char *usx_freq_seq[],
|
||||
const char *usx_templates[]);
|
||||
/**
|
||||
* More Comprehensive API for compressing array of strings
|
||||
*
|
||||
* See unishox2_compress() function for parameter definitions. \n
|
||||
* This function takes an additional parameter, i.e. 'prev_lines' - the usx_lnk_lst structure \n
|
||||
* See -g parameter in test_unishox2.c to find out how this can be used. \n
|
||||
* This function is used when an array of strings need to be compressed \n
|
||||
* and stored in a compressed array of bytes for use as a constant in other programs \n
|
||||
* where each element of the array can be decompressed and used at runtime.
|
||||
*/
|
||||
extern int unishox2_compress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen),
|
||||
const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[],
|
||||
const char *usx_freq_seq[], const char *usx_templates[], struct us_lnk_lst *prev_lines);
|
||||
/**
|
||||
* More Comprehensive API for de-compressing array of strings \n
|
||||
* This function is not be used in conjuction with unishox2_compress_lines()
|
||||
*
|
||||
* See unishox2_decompress() function for parameter definitions. \n
|
||||
* Typically an array is compressed using unishox2_compress_lines() and \n
|
||||
* a header (.h) file is generated using the resultant compressed array. \n
|
||||
* This header file can be used in another program with another decompress \n
|
||||
* routine which takes this compressed array as parameter and index to be \n
|
||||
* decompressed.
|
||||
*/
|
||||
extern int unishox2_decompress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen),
|
||||
const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[],
|
||||
const char *usx_freq_seq[], const char *usx_templates[], struct us_lnk_lst *prev_lines);
|
||||
|
||||
#endif
|
||||
@@ -6,7 +6,13 @@
|
||||
#include "main.h"
|
||||
#include "mesh/api/ethServerAPI.h"
|
||||
#include "target_specific.h"
|
||||
#ifdef USE_ARDUINO_ETHERNET
|
||||
#include <Ethernet.h> // arduino-libraries/Ethernet — supports W5100/W5200/W5500
|
||||
// Shorter DHCP timeout so LoRa startup isn't blocked when no DHCP server is present.
|
||||
#define ETH_DHCP_TIMEOUT_MS 10000
|
||||
#else
|
||||
#include <RAK13800_W5100S.h>
|
||||
#endif
|
||||
#include <SPI.h>
|
||||
|
||||
#if HAS_NETWORKING && !defined(USE_WS5500) && !defined(USE_CH390D)
|
||||
@@ -69,6 +75,13 @@ static int32_t reconnectETH()
|
||||
delay(100);
|
||||
#endif
|
||||
|
||||
#ifdef USE_ARDUINO_ETHERNET // Re-configure SPI0 for the W5500 module
|
||||
SPI.setRX(ETH_SPI0_MISO);
|
||||
SPI.setSCK(ETH_SPI0_SCK);
|
||||
SPI.setTX(ETH_SPI0_MOSI);
|
||||
SPI.begin();
|
||||
Ethernet.init(PIN_ETHERNET_SS);
|
||||
#else
|
||||
#ifdef RAK11310
|
||||
ETH_SPI_PORT.setSCK(PIN_SPI0_SCK);
|
||||
ETH_SPI_PORT.setTX(PIN_SPI0_MOSI);
|
||||
@@ -76,10 +89,15 @@ static int32_t reconnectETH()
|
||||
ETH_SPI_PORT.begin();
|
||||
#endif
|
||||
Ethernet.init(ETH_SPI_PORT, PIN_ETHERNET_SS);
|
||||
#endif
|
||||
|
||||
int status = 0;
|
||||
if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_DHCP) {
|
||||
#ifdef ETH_DHCP_TIMEOUT_MS
|
||||
status = Ethernet.begin(expectedMac, ETH_DHCP_TIMEOUT_MS);
|
||||
#else
|
||||
status = Ethernet.begin(expectedMac);
|
||||
#endif
|
||||
} else if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_STATIC) {
|
||||
Ethernet.begin(expectedMac, config.network.ipv4_config.ip, config.network.ipv4_config.dns,
|
||||
config.network.ipv4_config.gateway, config.network.ipv4_config.subnet);
|
||||
@@ -182,6 +200,13 @@ bool initEthernet()
|
||||
digitalWrite(PIN_ETHERNET_RESET, HIGH); // Reset Time.
|
||||
#endif
|
||||
|
||||
#ifdef USE_ARDUINO_ETHERNET // Configure SPI0 for the W5500 module
|
||||
SPI.setRX(ETH_SPI0_MISO);
|
||||
SPI.setSCK(ETH_SPI0_SCK);
|
||||
SPI.setTX(ETH_SPI0_MOSI);
|
||||
SPI.begin();
|
||||
Ethernet.init(PIN_ETHERNET_SS);
|
||||
#else
|
||||
#ifdef RAK11310 // Initialize the SPI port
|
||||
ETH_SPI_PORT.setSCK(PIN_SPI0_SCK);
|
||||
ETH_SPI_PORT.setTX(PIN_SPI0_MOSI);
|
||||
@@ -189,6 +214,7 @@ bool initEthernet()
|
||||
ETH_SPI_PORT.begin();
|
||||
#endif
|
||||
Ethernet.init(ETH_SPI_PORT, PIN_ETHERNET_SS);
|
||||
#endif
|
||||
|
||||
uint8_t mac[6];
|
||||
|
||||
@@ -201,7 +227,11 @@ bool initEthernet()
|
||||
|
||||
if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_DHCP) {
|
||||
LOG_INFO("Start Ethernet DHCP");
|
||||
#ifdef ETH_DHCP_TIMEOUT_MS
|
||||
status = Ethernet.begin(mac, ETH_DHCP_TIMEOUT_MS);
|
||||
#else
|
||||
status = Ethernet.begin(mac);
|
||||
#endif
|
||||
} else if (config.network.address_mode == meshtastic_Config_NetworkConfig_AddressMode_STATIC) {
|
||||
LOG_INFO("Start Ethernet Static");
|
||||
Ethernet.begin(mac, config.network.ipv4_config.ip, config.network.ipv4_config.dns, config.network.ipv4_config.gateway,
|
||||
|
||||
@@ -30,7 +30,7 @@ PB_BIND(meshtastic_AircraftTrack, meshtastic_AircraftTrack, AUTO)
|
||||
PB_BIND(meshtastic_CotGeoPoint, meshtastic_CotGeoPoint, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_DrawnShape, meshtastic_DrawnShape, 2)
|
||||
PB_BIND(meshtastic_DrawnShape, meshtastic_DrawnShape, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_Marker, meshtastic_Marker, AUTO)
|
||||
@@ -63,6 +63,15 @@ PB_BIND(meshtastic_TAKEnvironment, meshtastic_TAKEnvironment, AUTO)
|
||||
PB_BIND(meshtastic_SensorFov, meshtastic_SensorFov, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_TakTalkMessage, meshtastic_TakTalkMessage, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_TakTalkRoomData, meshtastic_TakTalkRoomData, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_Marti, meshtastic_Marti, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_TAKPacketV2, meshtastic_TAKPacketV2, 2)
|
||||
|
||||
|
||||
|
||||
@@ -325,7 +325,15 @@ typedef enum _meshtastic_CotType {
|
||||
meshtastic_CotType_CotType_b_a_o_c = 123,
|
||||
/* t-s: Task / engage request. Structured payload carried via the new
|
||||
TaskRequest typed variant. */
|
||||
meshtastic_CotType_CotType_t_s = 124
|
||||
meshtastic_CotType_CotType_t_s = 124,
|
||||
/* m-t-t: TAKTALK voice/text chat message. Payload carried via the
|
||||
TakTalkMessage typed variant (text, chatroom_id, lang, from_voice). */
|
||||
meshtastic_CotType_CotType_m_t_t = 125,
|
||||
/* y-: TAKTALK room/membership broadcast. Payload carried via the
|
||||
TakTalkRoomData typed variant (sender_callsign, room_id, room_name,
|
||||
participants). The CoT type literally has a trailing dash and no
|
||||
second atom — not a typo. */
|
||||
meshtastic_CotType_CotType_y = 126
|
||||
} meshtastic_CotType;
|
||||
|
||||
/* Geopoint and altitude source */
|
||||
@@ -553,6 +561,18 @@ typedef struct _meshtastic_GeoChat {
|
||||
/* Receipt kind discriminator. See ReceiptType doc. Default ReceiptType_None
|
||||
means this is a regular chat message, not a receipt. */
|
||||
meshtastic_GeoChat_ReceiptType receipt_type;
|
||||
/* BCP-47-ish language tag or human-readable name (e.g. "en", "English")
|
||||
that the originator's TAKTALK plugin recorded for the message. */
|
||||
pb_callback_t lang;
|
||||
/* TAKTALK chatroom UUID (e.g. "30b2755c-c547-44ef-a0cc-cdbd8a15616f") that
|
||||
the receiver's TAKTALK plugin uses to thread the message under the
|
||||
right room. Resolved to a friendly name via TakTalkRoomData broadcasts. */
|
||||
pb_callback_t room_id;
|
||||
/* TAKTALK voice profile pointer. Often empty in practice (the empty
|
||||
marker `<voice_profile_id/>` still signals TAKTALK origination), so
|
||||
receivers should treat empty-but-present as the equivalent of the
|
||||
marker rather than a missing field. */
|
||||
pb_callback_t voice_profile_id;
|
||||
} meshtastic_GeoChat;
|
||||
|
||||
/* ATAK Group
|
||||
@@ -715,12 +735,7 @@ typedef struct _meshtastic_DrawnShape {
|
||||
uint32_t fill_argb;
|
||||
/* Whether labels are rendered on this shape. */
|
||||
bool labels_on;
|
||||
/* Vertex list for polyline/polygon/rectangle shapes. Capped at 32 by
|
||||
the nanopb pool; senders MUST truncate longer inputs and set
|
||||
`truncated = true`. */
|
||||
pb_size_t vertices_count;
|
||||
meshtastic_CotGeoPoint vertices[32];
|
||||
/* True if the sender truncated `vertices` to fit the pool. */
|
||||
/* True if the sender truncated the vertex columns to fit the pool. */
|
||||
bool truncated; /* --- Bullseye-only fields. All ignored unless kind == Kind_Bullseye. --- */
|
||||
/* Bullseye distance in meters * 10 (e.g. 3285 = 328.5 m). 0 = unset. */
|
||||
uint32_t bullseye_distance_dm;
|
||||
@@ -734,6 +749,8 @@ typedef struct _meshtastic_DrawnShape {
|
||||
uint8_t bullseye_flags;
|
||||
/* Bullseye reference UID (anchor marker). Empty = anchor is self. */
|
||||
char bullseye_uid_ref[48];
|
||||
pb_callback_t vertex_lat_deltas;
|
||||
pb_callback_t vertex_lon_deltas;
|
||||
} meshtastic_DrawnShape;
|
||||
|
||||
/* Fixed point of interest: spot marker, waypoint, checkpoint, 2525 symbol,
|
||||
@@ -1068,6 +1085,87 @@ typedef struct _meshtastic_SensorFov {
|
||||
pb_callback_t model;
|
||||
} meshtastic_SensorFov;
|
||||
|
||||
/* TAKTALK chat message payload (CoT type m-t-t).
|
||||
|
||||
TAKTALK is an ATAK plugin for voice + text team messaging. The voice
|
||||
audio stream goes over UDP/RTP and is NOT carried by the mesh — only
|
||||
the text envelope (this message) is. `from_voice` marks messages sent
|
||||
via push-to-talk speech-to-text so receivers can render a mic icon
|
||||
next to the text.
|
||||
|
||||
Wire shape inside <event type="m-t-t">/<detail>:
|
||||
<callsign>...</callsign> - mapped to TAKPacketV2.callsign
|
||||
<lang>English</lang> - lang
|
||||
<text>...</text> - text
|
||||
<chatroom-id>1</chatroom-id> - chatroom_id
|
||||
<voice/> - presence sets from_voice = true */
|
||||
typedef struct _meshtastic_TakTalkMessage {
|
||||
/* The text body of the TAKTALK message (speech-to-text transcript when
|
||||
from_voice = true, typed message otherwise). */
|
||||
pb_callback_t text;
|
||||
/* TAKTALK chatroom identifier. May be a short id like "1" for the
|
||||
default room or a UUID like "30b2755c-c547-44ef-a0cc-cdbd8a15616f"
|
||||
for custom rooms (resolved by TakTalkRoomData broadcasts).
|
||||
Empty = broadcast room. */
|
||||
pb_callback_t chatroom_id;
|
||||
/* BCP-47-ish language tag or human-readable name (e.g. "en", "English").
|
||||
Empty = unspecified. */
|
||||
pb_callback_t lang;
|
||||
/* True when the source CoT carried a <voice/> marker, i.e. the message
|
||||
originated as push-to-talk speech-to-text. Lets receivers show a mic
|
||||
icon. Proto3 only encodes when true so empty payload cost is 0 bytes. */
|
||||
bool from_voice;
|
||||
} meshtastic_TakTalkMessage;
|
||||
|
||||
/* TAKTALK room/membership broadcast (CoT type y-).
|
||||
|
||||
Announces a TAKTALK chatroom's friendly name and roster so peers can
|
||||
resolve room UUIDs (used in TakTalkMessage.chatroom_id and
|
||||
GeoChat.room_id) to a display name and participant list. Not a chat
|
||||
message itself — these events are emitted by TAKTALK when rooms are
|
||||
created or memberships change. */
|
||||
typedef struct _meshtastic_TakTalkRoomData {
|
||||
/* Callsign of the device broadcasting the room state (typically the
|
||||
room owner / latest writer).
|
||||
|
||||
DEPRECATED in v0.3.2: always equals TAKPacketV2.callsign, so the wire
|
||||
byte was redundant. Builders stop emitting this field in v0.3.2;
|
||||
parsers still read it for one release so v0.3.1-encoded packets decode
|
||||
cleanly. To be removed entirely in v0.4.x. */
|
||||
pb_callback_t sender_callsign;
|
||||
/* Room UUID, matches TakTalkMessage.chatroom_id / GeoChat.room_id on
|
||||
messages routed into this room. */
|
||||
pb_callback_t room_id;
|
||||
/* Friendly display name for the room (e.g. "test", "Alpha Team"). */
|
||||
pb_callback_t room_name;
|
||||
/* Member callsigns. Wire-encoded as repeated strings; the underlying
|
||||
CoT carries them as a single <chatroom-participants>A,B,C</> element
|
||||
which parsers split / builders join on ','. */
|
||||
pb_callback_t participants;
|
||||
} meshtastic_TakTalkRoomData;
|
||||
|
||||
/* ATAK directed-routing recipient list (CoT <marti><dest callsign='X'/>…</marti>).
|
||||
|
||||
Present when an event is addressed to specific TAK users rather than the
|
||||
broadcast group. TAKTALK gates voice TTS on this element matching the
|
||||
receiver's callsign; directed b-t-f chats use it for the same purpose. A
|
||||
missing <marti> means "broadcast to all peers", which is the default for
|
||||
PLI, alerts, drawings, and most situational-awareness events.
|
||||
|
||||
Carried as repeated strings (not indexes into a per-packet table) because
|
||||
the typical event has 1-2 destinations and table overhead would erase the
|
||||
savings. Receivers that need the original XML element rebuild it from
|
||||
dest_callsign on emit. */
|
||||
typedef struct _meshtastic_Marti {
|
||||
/* Recipient callsigns. Order is preserved end-to-end so receivers can show
|
||||
primary-vs-cc distinction the same way ATAK does.
|
||||
|
||||
If dest_callsign is [TAKPacketV2.callsign] (self-addressed, unusual but
|
||||
legal — e.g. ATAK echoing back to its own room), the builder still emits
|
||||
the element so loopback shapes round-trip cleanly. */
|
||||
pb_callback_t dest_callsign;
|
||||
} meshtastic_Marti;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(220) meshtastic_TAKPacketV2_raw_detail_t;
|
||||
/* ATAK v2 packet with expanded CoT field support and zstd dictionary compression.
|
||||
Sent on ATAK_PLUGIN_V2 port. The wire payload is:
|
||||
@@ -1089,7 +1187,14 @@ typedef struct _meshtastic_TAKPacketV2 {
|
||||
int32_t latitude_i;
|
||||
/* Longitude, multiply by 1e-7 to get degrees in floating point */
|
||||
int32_t longitude_i;
|
||||
/* Altitude in meters (HAE) */
|
||||
/* Altitude in meters (HAE). ATAK's "no altitude" sentinel is hae=9999999.0.
|
||||
|
||||
NOTE: an earlier v0.4.0 attempt made this `optional` to omit the 9999999
|
||||
sentinel from the wire, but measurement showed it was net-negative: the
|
||||
zstd dictionary already compresses the literal 9999999 to ~nothing, while
|
||||
proto3 `optional` forces a genuine 0 m HAE (common on routes/drawings that
|
||||
carry hae="0.0" or omit hae → parsed as 0) to encode explicitly (+2 bytes),
|
||||
which REGRESSED the worst-case route fixture. Kept as a plain field. */
|
||||
int32_t altitude;
|
||||
/* Speed in cm/s */
|
||||
uint32_t speed;
|
||||
@@ -1135,10 +1240,18 @@ typedef struct _meshtastic_TAKPacketV2 {
|
||||
/* Sensor field-of-view cone (camera, FLIR, laser, etc.). From <sensor>. */
|
||||
bool has_sensor_fov;
|
||||
meshtastic_SensorFov sensor_fov;
|
||||
/* Directed-routing recipient list (CoT <marti><dest callsign='X'/>…</marti>).
|
||||
Empty / unset = broadcast to all peers (the default for situational-awareness
|
||||
events). Populated for TAKTALK m-t-t, directed b-t-f DMs, and any other CoT
|
||||
shape that ATAK addresses to specific recipients. TAKTALK gates voice TTS
|
||||
playback on this element matching the receiver's callsign, so dropping it
|
||||
silently breaks voice messaging end-to-end.
|
||||
|
||||
See Marti. */
|
||||
bool has_marti;
|
||||
meshtastic_Marti marti;
|
||||
pb_size_t which_payload_variant;
|
||||
union {
|
||||
/* Position report (true = PLI, no extra fields beyond the common ones above) */
|
||||
bool pli;
|
||||
/* ATAK GeoChat message */
|
||||
meshtastic_GeoChat chat;
|
||||
/* Aircraft track data (ADS-B, military air) */
|
||||
@@ -1163,6 +1276,14 @@ typedef struct _meshtastic_TAKPacketV2 {
|
||||
meshtastic_EmergencyAlert emergency;
|
||||
/* Task / engage request. See TaskRequest. */
|
||||
meshtastic_TaskRequest task;
|
||||
/* TAKTALK chat message (CoT type m-t-t). See TakTalkMessage.
|
||||
Voice audio itself rides UDP/RTP outside the mesh; this carries the
|
||||
text envelope plus a from_voice marker for receiver UX. */
|
||||
meshtastic_TakTalkMessage taktalk;
|
||||
/* TAKTALK room/membership broadcast (CoT type y-). See TakTalkRoomData.
|
||||
Resolves room UUIDs (used in TakTalkMessage.chatroom_id and
|
||||
GeoChat.room_id) to display name + roster on receivers. */
|
||||
meshtastic_TakTalkRoomData taktalk_room;
|
||||
} payload_variant;
|
||||
} meshtastic_TAKPacketV2;
|
||||
|
||||
@@ -1185,8 +1306,8 @@ extern "C" {
|
||||
#define _meshtastic_CotHow_ARRAYSIZE ((meshtastic_CotHow)(meshtastic_CotHow_CotHow_m_s+1))
|
||||
|
||||
#define _meshtastic_CotType_MIN meshtastic_CotType_CotType_Other
|
||||
#define _meshtastic_CotType_MAX meshtastic_CotType_CotType_t_s
|
||||
#define _meshtastic_CotType_ARRAYSIZE ((meshtastic_CotType)(meshtastic_CotType_CotType_t_s+1))
|
||||
#define _meshtastic_CotType_MAX meshtastic_CotType_CotType_y
|
||||
#define _meshtastic_CotType_ARRAYSIZE ((meshtastic_CotType)(meshtastic_CotType_CotType_y+1))
|
||||
|
||||
#define _meshtastic_GeoPointSource_MIN meshtastic_GeoPointSource_GeoPointSource_Unspecified
|
||||
#define _meshtastic_GeoPointSource_MAX meshtastic_GeoPointSource_GeoPointSource_NETWORK
|
||||
@@ -1282,6 +1403,9 @@ extern "C" {
|
||||
|
||||
#define meshtastic_SensorFov_type_ENUMTYPE meshtastic_SensorFov_SensorType
|
||||
|
||||
|
||||
|
||||
|
||||
#define meshtastic_TAKPacketV2_cot_type_id_ENUMTYPE meshtastic_CotType
|
||||
#define meshtastic_TAKPacketV2_how_ENUMTYPE meshtastic_CotHow
|
||||
#define meshtastic_TAKPacketV2_team_ENUMTYPE meshtastic_Team
|
||||
@@ -1292,14 +1416,14 @@ extern "C" {
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define meshtastic_TAKPacket_init_default {0, false, meshtastic_Contact_init_default, false, meshtastic_Group_init_default, false, meshtastic_Status_init_default, 0, {meshtastic_PLI_init_default}}
|
||||
#define meshtastic_GeoChat_init_default {"", false, "", false, "", "", _meshtastic_GeoChat_ReceiptType_MIN}
|
||||
#define meshtastic_GeoChat_init_default {"", false, "", false, "", "", _meshtastic_GeoChat_ReceiptType_MIN, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||
#define meshtastic_Group_init_default {_meshtastic_MemberRole_MIN, _meshtastic_Team_MIN}
|
||||
#define meshtastic_Status_init_default {0}
|
||||
#define meshtastic_Contact_init_default {"", ""}
|
||||
#define meshtastic_PLI_init_default {0, 0, 0, 0, 0}
|
||||
#define meshtastic_AircraftTrack_init_default {"", "", "", "", 0, "", 0, 0, ""}
|
||||
#define meshtastic_CotGeoPoint_init_default {0, 0}
|
||||
#define meshtastic_DrawnShape_init_default {_meshtastic_DrawnShape_Kind_MIN, _meshtastic_DrawnShape_StyleMode_MIN, 0, 0, 0, _meshtastic_Team_MIN, 0, 0, _meshtastic_Team_MIN, 0, 0, 0, {meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default}, 0, 0, 0, 0, ""}
|
||||
#define meshtastic_DrawnShape_init_default {_meshtastic_DrawnShape_Kind_MIN, _meshtastic_DrawnShape_StyleMode_MIN, 0, 0, 0, _meshtastic_Team_MIN, 0, 0, _meshtastic_Team_MIN, 0, 0, 0, 0, 0, 0, "", {{NULL}, NULL}, {{NULL}, NULL}}
|
||||
#define meshtastic_Marker_init_default {_meshtastic_Marker_Kind_MIN, _meshtastic_Team_MIN, 0, 0, "", "", "", ""}
|
||||
#define meshtastic_RangeAndBearing_init_default {false, meshtastic_CotGeoPoint_init_default, "", 0, 0, _meshtastic_Team_MIN, 0, 0}
|
||||
#define meshtastic_Route_init_default {_meshtastic_Route_Method_MIN, _meshtastic_Route_Direction_MIN, "", 0, 0, {meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default}, 0}
|
||||
@@ -1310,16 +1434,19 @@ extern "C" {
|
||||
#define meshtastic_TaskRequest_init_default {"", "", "", _meshtastic_TaskRequest_Priority_MIN, _meshtastic_TaskRequest_Status_MIN, ""}
|
||||
#define meshtastic_TAKEnvironment_init_default {0, 0, 0}
|
||||
#define meshtastic_SensorFov_init_default {_meshtastic_SensorFov_SensorType_MIN, 0, false, 0, 0, 0, 0, 0, {{NULL}, NULL}}
|
||||
#define meshtastic_TAKPacketV2_init_default {_meshtastic_CotType_MIN, _meshtastic_CotHow_MIN, "", _meshtastic_Team_MIN, _meshtastic_MemberRole_MIN, 0, 0, 0, 0, 0, 0, _meshtastic_GeoPointSource_MIN, _meshtastic_GeoPointSource_MIN, "", "", 0, "", "", "", "", "", "", "", {{NULL}, NULL}, false, meshtastic_TAKEnvironment_init_default, false, meshtastic_SensorFov_init_default, 0, {0}}
|
||||
#define meshtastic_TakTalkMessage_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0}
|
||||
#define meshtastic_TakTalkRoomData_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||
#define meshtastic_Marti_init_default {{{NULL}, NULL}}
|
||||
#define meshtastic_TAKPacketV2_init_default {_meshtastic_CotType_MIN, _meshtastic_CotHow_MIN, "", _meshtastic_Team_MIN, _meshtastic_MemberRole_MIN, 0, 0, 0, 0, 0, 0, _meshtastic_GeoPointSource_MIN, _meshtastic_GeoPointSource_MIN, "", "", 0, "", "", "", "", "", "", "", {{NULL}, NULL}, false, meshtastic_TAKEnvironment_init_default, false, meshtastic_SensorFov_init_default, false, meshtastic_Marti_init_default, 0, {meshtastic_GeoChat_init_default}}
|
||||
#define meshtastic_TAKPacket_init_zero {0, false, meshtastic_Contact_init_zero, false, meshtastic_Group_init_zero, false, meshtastic_Status_init_zero, 0, {meshtastic_PLI_init_zero}}
|
||||
#define meshtastic_GeoChat_init_zero {"", false, "", false, "", "", _meshtastic_GeoChat_ReceiptType_MIN}
|
||||
#define meshtastic_GeoChat_init_zero {"", false, "", false, "", "", _meshtastic_GeoChat_ReceiptType_MIN, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||
#define meshtastic_Group_init_zero {_meshtastic_MemberRole_MIN, _meshtastic_Team_MIN}
|
||||
#define meshtastic_Status_init_zero {0}
|
||||
#define meshtastic_Contact_init_zero {"", ""}
|
||||
#define meshtastic_PLI_init_zero {0, 0, 0, 0, 0}
|
||||
#define meshtastic_AircraftTrack_init_zero {"", "", "", "", 0, "", 0, 0, ""}
|
||||
#define meshtastic_CotGeoPoint_init_zero {0, 0}
|
||||
#define meshtastic_DrawnShape_init_zero {_meshtastic_DrawnShape_Kind_MIN, _meshtastic_DrawnShape_StyleMode_MIN, 0, 0, 0, _meshtastic_Team_MIN, 0, 0, _meshtastic_Team_MIN, 0, 0, 0, {meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero}, 0, 0, 0, 0, ""}
|
||||
#define meshtastic_DrawnShape_init_zero {_meshtastic_DrawnShape_Kind_MIN, _meshtastic_DrawnShape_StyleMode_MIN, 0, 0, 0, _meshtastic_Team_MIN, 0, 0, _meshtastic_Team_MIN, 0, 0, 0, 0, 0, 0, "", {{NULL}, NULL}, {{NULL}, NULL}}
|
||||
#define meshtastic_Marker_init_zero {_meshtastic_Marker_Kind_MIN, _meshtastic_Team_MIN, 0, 0, "", "", "", ""}
|
||||
#define meshtastic_RangeAndBearing_init_zero {false, meshtastic_CotGeoPoint_init_zero, "", 0, 0, _meshtastic_Team_MIN, 0, 0}
|
||||
#define meshtastic_Route_init_zero {_meshtastic_Route_Method_MIN, _meshtastic_Route_Direction_MIN, "", 0, 0, {meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero}, 0}
|
||||
@@ -1330,7 +1457,10 @@ extern "C" {
|
||||
#define meshtastic_TaskRequest_init_zero {"", "", "", _meshtastic_TaskRequest_Priority_MIN, _meshtastic_TaskRequest_Status_MIN, ""}
|
||||
#define meshtastic_TAKEnvironment_init_zero {0, 0, 0}
|
||||
#define meshtastic_SensorFov_init_zero {_meshtastic_SensorFov_SensorType_MIN, 0, false, 0, 0, 0, 0, 0, {{NULL}, NULL}}
|
||||
#define meshtastic_TAKPacketV2_init_zero {_meshtastic_CotType_MIN, _meshtastic_CotHow_MIN, "", _meshtastic_Team_MIN, _meshtastic_MemberRole_MIN, 0, 0, 0, 0, 0, 0, _meshtastic_GeoPointSource_MIN, _meshtastic_GeoPointSource_MIN, "", "", 0, "", "", "", "", "", "", "", {{NULL}, NULL}, false, meshtastic_TAKEnvironment_init_zero, false, meshtastic_SensorFov_init_zero, 0, {0}}
|
||||
#define meshtastic_TakTalkMessage_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0}
|
||||
#define meshtastic_TakTalkRoomData_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||
#define meshtastic_Marti_init_zero {{{NULL}, NULL}}
|
||||
#define meshtastic_TAKPacketV2_init_zero {_meshtastic_CotType_MIN, _meshtastic_CotHow_MIN, "", _meshtastic_Team_MIN, _meshtastic_MemberRole_MIN, 0, 0, 0, 0, 0, 0, _meshtastic_GeoPointSource_MIN, _meshtastic_GeoPointSource_MIN, "", "", 0, "", "", "", "", "", "", "", {{NULL}, NULL}, false, meshtastic_TAKEnvironment_init_zero, false, meshtastic_SensorFov_init_zero, false, meshtastic_Marti_init_zero, 0, {meshtastic_GeoChat_init_zero}}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define meshtastic_GeoChat_message_tag 1
|
||||
@@ -1338,6 +1468,9 @@ extern "C" {
|
||||
#define meshtastic_GeoChat_to_callsign_tag 3
|
||||
#define meshtastic_GeoChat_receipt_for_uid_tag 4
|
||||
#define meshtastic_GeoChat_receipt_type_tag 5
|
||||
#define meshtastic_GeoChat_lang_tag 6
|
||||
#define meshtastic_GeoChat_room_id_tag 7
|
||||
#define meshtastic_GeoChat_voice_profile_id_tag 8
|
||||
#define meshtastic_Group_role_tag 1
|
||||
#define meshtastic_Group_team_tag 2
|
||||
#define meshtastic_Status_battery_tag 1
|
||||
@@ -1377,12 +1510,13 @@ extern "C" {
|
||||
#define meshtastic_DrawnShape_fill_color_tag 9
|
||||
#define meshtastic_DrawnShape_fill_argb_tag 10
|
||||
#define meshtastic_DrawnShape_labels_on_tag 11
|
||||
#define meshtastic_DrawnShape_vertices_tag 12
|
||||
#define meshtastic_DrawnShape_truncated_tag 13
|
||||
#define meshtastic_DrawnShape_bullseye_distance_dm_tag 14
|
||||
#define meshtastic_DrawnShape_bullseye_bearing_ref_tag 15
|
||||
#define meshtastic_DrawnShape_bullseye_flags_tag 16
|
||||
#define meshtastic_DrawnShape_bullseye_uid_ref_tag 17
|
||||
#define meshtastic_DrawnShape_vertex_lat_deltas_tag 18
|
||||
#define meshtastic_DrawnShape_vertex_lon_deltas_tag 19
|
||||
#define meshtastic_Marker_kind_tag 1
|
||||
#define meshtastic_Marker_color_tag 2
|
||||
#define meshtastic_Marker_color_argb_tag 3
|
||||
@@ -1467,6 +1601,15 @@ extern "C" {
|
||||
#define meshtastic_SensorFov_elevation_deg_tag 6
|
||||
#define meshtastic_SensorFov_roll_deg_tag 7
|
||||
#define meshtastic_SensorFov_model_tag 8
|
||||
#define meshtastic_TakTalkMessage_text_tag 1
|
||||
#define meshtastic_TakTalkMessage_chatroom_id_tag 2
|
||||
#define meshtastic_TakTalkMessage_lang_tag 3
|
||||
#define meshtastic_TakTalkMessage_from_voice_tag 4
|
||||
#define meshtastic_TakTalkRoomData_sender_callsign_tag 1
|
||||
#define meshtastic_TakTalkRoomData_room_id_tag 2
|
||||
#define meshtastic_TakTalkRoomData_room_name_tag 3
|
||||
#define meshtastic_TakTalkRoomData_participants_tag 4
|
||||
#define meshtastic_Marti_dest_callsign_tag 1
|
||||
#define meshtastic_TAKPacketV2_cot_type_id_tag 1
|
||||
#define meshtastic_TAKPacketV2_how_tag 2
|
||||
#define meshtastic_TAKPacketV2_callsign_tag 3
|
||||
@@ -1493,7 +1636,7 @@ extern "C" {
|
||||
#define meshtastic_TAKPacketV2_remarks_tag 24
|
||||
#define meshtastic_TAKPacketV2_environment_tag 25
|
||||
#define meshtastic_TAKPacketV2_sensor_fov_tag 26
|
||||
#define meshtastic_TAKPacketV2_pli_tag 30
|
||||
#define meshtastic_TAKPacketV2_marti_tag 29
|
||||
#define meshtastic_TAKPacketV2_chat_tag 31
|
||||
#define meshtastic_TAKPacketV2_aircraft_tag 32
|
||||
#define meshtastic_TAKPacketV2_raw_detail_tag 33
|
||||
@@ -1504,6 +1647,8 @@ extern "C" {
|
||||
#define meshtastic_TAKPacketV2_casevac_tag 38
|
||||
#define meshtastic_TAKPacketV2_emergency_tag 39
|
||||
#define meshtastic_TAKPacketV2_task_tag 40
|
||||
#define meshtastic_TAKPacketV2_taktalk_tag 41
|
||||
#define meshtastic_TAKPacketV2_taktalk_room_tag 42
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define meshtastic_TAKPacket_FIELDLIST(X, a) \
|
||||
@@ -1527,8 +1672,11 @@ X(a, STATIC, SINGULAR, STRING, message, 1) \
|
||||
X(a, STATIC, OPTIONAL, STRING, to, 2) \
|
||||
X(a, STATIC, OPTIONAL, STRING, to_callsign, 3) \
|
||||
X(a, STATIC, SINGULAR, STRING, receipt_for_uid, 4) \
|
||||
X(a, STATIC, SINGULAR, UENUM, receipt_type, 5)
|
||||
#define meshtastic_GeoChat_CALLBACK NULL
|
||||
X(a, STATIC, SINGULAR, UENUM, receipt_type, 5) \
|
||||
X(a, CALLBACK, OPTIONAL, STRING, lang, 6) \
|
||||
X(a, CALLBACK, OPTIONAL, STRING, room_id, 7) \
|
||||
X(a, CALLBACK, OPTIONAL, STRING, voice_profile_id, 8)
|
||||
#define meshtastic_GeoChat_CALLBACK pb_default_field_callback
|
||||
#define meshtastic_GeoChat_DEFAULT NULL
|
||||
|
||||
#define meshtastic_Group_FIELDLIST(X, a) \
|
||||
@@ -1588,15 +1736,15 @@ X(a, STATIC, SINGULAR, UINT32, stroke_weight_x10, 8) \
|
||||
X(a, STATIC, SINGULAR, UENUM, fill_color, 9) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, fill_argb, 10) \
|
||||
X(a, STATIC, SINGULAR, BOOL, labels_on, 11) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, vertices, 12) \
|
||||
X(a, STATIC, SINGULAR, BOOL, truncated, 13) \
|
||||
X(a, STATIC, SINGULAR, UINT32, bullseye_distance_dm, 14) \
|
||||
X(a, STATIC, SINGULAR, UINT32, bullseye_bearing_ref, 15) \
|
||||
X(a, STATIC, SINGULAR, UINT32, bullseye_flags, 16) \
|
||||
X(a, STATIC, SINGULAR, STRING, bullseye_uid_ref, 17)
|
||||
#define meshtastic_DrawnShape_CALLBACK NULL
|
||||
X(a, STATIC, SINGULAR, STRING, bullseye_uid_ref, 17) \
|
||||
X(a, CALLBACK, REPEATED, SINT32, vertex_lat_deltas, 18) \
|
||||
X(a, CALLBACK, REPEATED, SINT32, vertex_lon_deltas, 19)
|
||||
#define meshtastic_DrawnShape_CALLBACK pb_default_field_callback
|
||||
#define meshtastic_DrawnShape_DEFAULT NULL
|
||||
#define meshtastic_DrawnShape_vertices_MSGTYPE meshtastic_CotGeoPoint
|
||||
|
||||
#define meshtastic_Marker_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, kind, 1) \
|
||||
@@ -1726,6 +1874,27 @@ X(a, CALLBACK, SINGULAR, STRING, model, 8)
|
||||
#define meshtastic_SensorFov_CALLBACK pb_default_field_callback
|
||||
#define meshtastic_SensorFov_DEFAULT NULL
|
||||
|
||||
#define meshtastic_TakTalkMessage_FIELDLIST(X, a) \
|
||||
X(a, CALLBACK, SINGULAR, STRING, text, 1) \
|
||||
X(a, CALLBACK, SINGULAR, STRING, chatroom_id, 2) \
|
||||
X(a, CALLBACK, SINGULAR, STRING, lang, 3) \
|
||||
X(a, STATIC, SINGULAR, BOOL, from_voice, 4)
|
||||
#define meshtastic_TakTalkMessage_CALLBACK pb_default_field_callback
|
||||
#define meshtastic_TakTalkMessage_DEFAULT NULL
|
||||
|
||||
#define meshtastic_TakTalkRoomData_FIELDLIST(X, a) \
|
||||
X(a, CALLBACK, SINGULAR, STRING, sender_callsign, 1) \
|
||||
X(a, CALLBACK, SINGULAR, STRING, room_id, 2) \
|
||||
X(a, CALLBACK, SINGULAR, STRING, room_name, 3) \
|
||||
X(a, CALLBACK, REPEATED, STRING, participants, 4)
|
||||
#define meshtastic_TakTalkRoomData_CALLBACK pb_default_field_callback
|
||||
#define meshtastic_TakTalkRoomData_DEFAULT NULL
|
||||
|
||||
#define meshtastic_Marti_FIELDLIST(X, a) \
|
||||
X(a, CALLBACK, REPEATED, STRING, dest_callsign, 1)
|
||||
#define meshtastic_Marti_CALLBACK pb_default_field_callback
|
||||
#define meshtastic_Marti_DEFAULT NULL
|
||||
|
||||
#define meshtastic_TAKPacketV2_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, cot_type_id, 1) \
|
||||
X(a, STATIC, SINGULAR, UENUM, how, 2) \
|
||||
@@ -1753,7 +1922,7 @@ X(a, STATIC, SINGULAR, STRING, cot_type_str, 23) \
|
||||
X(a, CALLBACK, SINGULAR, STRING, remarks, 24) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, environment, 25) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, sensor_fov, 26) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,pli,payload_variant.pli), 30) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, marti, 29) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,chat,payload_variant.chat), 31) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,aircraft,payload_variant.aircraft), 32) \
|
||||
X(a, STATIC, ONEOF, BYTES, (payload_variant,raw_detail,payload_variant.raw_detail), 33) \
|
||||
@@ -1763,11 +1932,14 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,rab,payload_variant.rab), 3
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,route,payload_variant.route), 37) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,casevac,payload_variant.casevac), 38) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,emergency,payload_variant.emergency), 39) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,task,payload_variant.task), 40)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,task,payload_variant.task), 40) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,taktalk,payload_variant.taktalk), 41) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,taktalk_room,payload_variant.taktalk_room), 42)
|
||||
#define meshtastic_TAKPacketV2_CALLBACK pb_default_field_callback
|
||||
#define meshtastic_TAKPacketV2_DEFAULT NULL
|
||||
#define meshtastic_TAKPacketV2_environment_MSGTYPE meshtastic_TAKEnvironment
|
||||
#define meshtastic_TAKPacketV2_sensor_fov_MSGTYPE meshtastic_SensorFov
|
||||
#define meshtastic_TAKPacketV2_marti_MSGTYPE meshtastic_Marti
|
||||
#define meshtastic_TAKPacketV2_payload_variant_chat_MSGTYPE meshtastic_GeoChat
|
||||
#define meshtastic_TAKPacketV2_payload_variant_aircraft_MSGTYPE meshtastic_AircraftTrack
|
||||
#define meshtastic_TAKPacketV2_payload_variant_shape_MSGTYPE meshtastic_DrawnShape
|
||||
@@ -1777,6 +1949,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,task,payload_variant.task),
|
||||
#define meshtastic_TAKPacketV2_payload_variant_casevac_MSGTYPE meshtastic_CasevacReport
|
||||
#define meshtastic_TAKPacketV2_payload_variant_emergency_MSGTYPE meshtastic_EmergencyAlert
|
||||
#define meshtastic_TAKPacketV2_payload_variant_task_MSGTYPE meshtastic_TaskRequest
|
||||
#define meshtastic_TAKPacketV2_payload_variant_taktalk_MSGTYPE meshtastic_TakTalkMessage
|
||||
#define meshtastic_TAKPacketV2_payload_variant_taktalk_room_MSGTYPE meshtastic_TakTalkRoomData
|
||||
|
||||
extern const pb_msgdesc_t meshtastic_TAKPacket_msg;
|
||||
extern const pb_msgdesc_t meshtastic_GeoChat_msg;
|
||||
@@ -1797,6 +1971,9 @@ extern const pb_msgdesc_t meshtastic_EmergencyAlert_msg;
|
||||
extern const pb_msgdesc_t meshtastic_TaskRequest_msg;
|
||||
extern const pb_msgdesc_t meshtastic_TAKEnvironment_msg;
|
||||
extern const pb_msgdesc_t meshtastic_SensorFov_msg;
|
||||
extern const pb_msgdesc_t meshtastic_TakTalkMessage_msg;
|
||||
extern const pb_msgdesc_t meshtastic_TakTalkRoomData_msg;
|
||||
extern const pb_msgdesc_t meshtastic_Marti_msg;
|
||||
extern const pb_msgdesc_t meshtastic_TAKPacketV2_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
@@ -1819,20 +1996,27 @@ extern const pb_msgdesc_t meshtastic_TAKPacketV2_msg;
|
||||
#define meshtastic_TaskRequest_fields &meshtastic_TaskRequest_msg
|
||||
#define meshtastic_TAKEnvironment_fields &meshtastic_TAKEnvironment_msg
|
||||
#define meshtastic_SensorFov_fields &meshtastic_SensorFov_msg
|
||||
#define meshtastic_TakTalkMessage_fields &meshtastic_TakTalkMessage_msg
|
||||
#define meshtastic_TakTalkRoomData_fields &meshtastic_TakTalkRoomData_msg
|
||||
#define meshtastic_Marti_fields &meshtastic_Marti_msg
|
||||
#define meshtastic_TAKPacketV2_fields &meshtastic_TAKPacketV2_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
/* meshtastic_TAKPacket_size depends on runtime parameters */
|
||||
/* meshtastic_GeoChat_size depends on runtime parameters */
|
||||
/* meshtastic_DrawnShape_size depends on runtime parameters */
|
||||
/* meshtastic_CasevacReport_size depends on runtime parameters */
|
||||
/* meshtastic_ZMistEntry_size depends on runtime parameters */
|
||||
/* meshtastic_SensorFov_size depends on runtime parameters */
|
||||
/* meshtastic_TakTalkMessage_size depends on runtime parameters */
|
||||
/* meshtastic_TakTalkRoomData_size depends on runtime parameters */
|
||||
/* meshtastic_Marti_size depends on runtime parameters */
|
||||
/* meshtastic_TAKPacketV2_size depends on runtime parameters */
|
||||
#define MESHTASTIC_MESHTASTIC_ATAK_PB_H_MAX_SIZE meshtastic_Route_size
|
||||
#define meshtastic_AircraftTrack_size 134
|
||||
#define meshtastic_Contact_size 242
|
||||
#define meshtastic_CotGeoPoint_size 12
|
||||
#define meshtastic_DrawnShape_size 553
|
||||
#define meshtastic_EmergencyAlert_size 100
|
||||
#define meshtastic_GeoChat_size 495
|
||||
#define meshtastic_Group_size 4
|
||||
#define meshtastic_Marker_size 191
|
||||
#define meshtastic_PLI_size 31
|
||||
@@ -1841,7 +2025,6 @@ extern const pb_msgdesc_t meshtastic_TAKPacketV2_msg;
|
||||
#define meshtastic_Route_size 1379
|
||||
#define meshtastic_Status_size 3
|
||||
#define meshtastic_TAKEnvironment_size 18
|
||||
#define meshtastic_TAKPacket_size 756
|
||||
#define meshtastic_TaskRequest_size 132
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -198,7 +198,9 @@ typedef enum _meshtastic_Config_DisplayConfig_OledType {
|
||||
/* Can not be auto detected but set by proto. Used for 128x64 screens */
|
||||
meshtastic_Config_DisplayConfig_OledType_OLED_SH1107 = 3,
|
||||
/* Can not be auto detected but set by proto. Used for 128x128 screens */
|
||||
meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_128_128 = 4
|
||||
meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_128_128 = 4,
|
||||
/* Can not be auto detected but set by proto. Used for 64x128 rotated screens */
|
||||
meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_ROTATED = 5
|
||||
} meshtastic_Config_DisplayConfig_OledType;
|
||||
|
||||
typedef enum _meshtastic_Config_DisplayConfig_DisplayMode {
|
||||
@@ -288,15 +290,26 @@ typedef enum _meshtastic_Config_LoRaConfig_RegionCode {
|
||||
meshtastic_Config_LoRaConfig_RegionCode_BR_902 = 26,
|
||||
/* ITU Region 1 Amateur Radio 2m band (144-146 MHz) */
|
||||
meshtastic_Config_LoRaConfig_RegionCode_ITU1_2M = 27,
|
||||
/* ITU Region 2 / 3 Amateur Radio 2m band (144-148 MHz) */
|
||||
meshtastic_Config_LoRaConfig_RegionCode_ITU23_2M = 28,
|
||||
/* ITU Region 2 Amateur Radio 2m band (144-148 MHz) */
|
||||
meshtastic_Config_LoRaConfig_RegionCode_ITU2_2M = 28,
|
||||
/* EU 866MHz band (Band no. 47b of 2006/771/EC and subsequent amendments) for Non-specific short-range devices (SRD) */
|
||||
meshtastic_Config_LoRaConfig_RegionCode_EU_866 = 29,
|
||||
/* EU 874MHz and 917MHz bands (Band no. 1 and 4 of 2022/172/EC and subsequent amendments) for Non-specific short-range devices (SRD) */
|
||||
meshtastic_Config_LoRaConfig_RegionCode_EU_874 = 30,
|
||||
meshtastic_Config_LoRaConfig_RegionCode_EU_917 = 31,
|
||||
/* EU 868MHz band, with narrow presets */
|
||||
meshtastic_Config_LoRaConfig_RegionCode_EU_N_868 = 32
|
||||
meshtastic_Config_LoRaConfig_RegionCode_EU_N_868 = 32,
|
||||
/* ITU Region 3 Amateur Radio 2m band (144-148 MHz) */
|
||||
meshtastic_Config_LoRaConfig_RegionCode_ITU3_2M = 33,
|
||||
/* ITU Region 1 Amateur Radio 70cm band (430-440 MHz) */
|
||||
meshtastic_Config_LoRaConfig_RegionCode_ITU1_70CM = 34,
|
||||
/* ITU Region 2 Amateur Radio 70cm band (420-450 MHz)
|
||||
Note: Some countries do not allocate 420-430 MHz or 440-450 MHz.
|
||||
Check local law! */
|
||||
meshtastic_Config_LoRaConfig_RegionCode_ITU2_70CM = 35,
|
||||
/* ITU Region 3 Amateur Radio 70cm band (430-450 MHz)
|
||||
Note: Some countries do not allocate 440-450 MHz. Check local law! */
|
||||
meshtastic_Config_LoRaConfig_RegionCode_ITU3_70CM = 36
|
||||
} meshtastic_Config_LoRaConfig_RegionCode;
|
||||
|
||||
/* Standard predefined channel settings
|
||||
@@ -720,8 +733,8 @@ extern "C" {
|
||||
#define _meshtastic_Config_DisplayConfig_DisplayUnits_ARRAYSIZE ((meshtastic_Config_DisplayConfig_DisplayUnits)(meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL+1))
|
||||
|
||||
#define _meshtastic_Config_DisplayConfig_OledType_MIN meshtastic_Config_DisplayConfig_OledType_OLED_AUTO
|
||||
#define _meshtastic_Config_DisplayConfig_OledType_MAX meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_128_128
|
||||
#define _meshtastic_Config_DisplayConfig_OledType_ARRAYSIZE ((meshtastic_Config_DisplayConfig_OledType)(meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_128_128+1))
|
||||
#define _meshtastic_Config_DisplayConfig_OledType_MAX meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_ROTATED
|
||||
#define _meshtastic_Config_DisplayConfig_OledType_ARRAYSIZE ((meshtastic_Config_DisplayConfig_OledType)(meshtastic_Config_DisplayConfig_OledType_OLED_SH1107_ROTATED+1))
|
||||
|
||||
#define _meshtastic_Config_DisplayConfig_DisplayMode_MIN meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT
|
||||
#define _meshtastic_Config_DisplayConfig_DisplayMode_MAX meshtastic_Config_DisplayConfig_DisplayMode_COLOR
|
||||
@@ -732,8 +745,8 @@ extern "C" {
|
||||
#define _meshtastic_Config_DisplayConfig_CompassOrientation_ARRAYSIZE ((meshtastic_Config_DisplayConfig_CompassOrientation)(meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED+1))
|
||||
|
||||
#define _meshtastic_Config_LoRaConfig_RegionCode_MIN meshtastic_Config_LoRaConfig_RegionCode_UNSET
|
||||
#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_EU_N_868
|
||||
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_EU_N_868+1))
|
||||
#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_ITU3_70CM
|
||||
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_ITU3_70CM+1))
|
||||
|
||||
#define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST
|
||||
#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_NARROW_SLOW
|
||||
|
||||
@@ -321,6 +321,10 @@ typedef enum _meshtastic_HardwareModel {
|
||||
meshtastic_HardwareModel_HELTEC_MESH_NODE_T1 = 133,
|
||||
/* B&Q Consulting Station G3: TBD */
|
||||
meshtastic_HardwareModel_STATION_G3 = 134,
|
||||
/* Lilygo T-Impulse-Plus */
|
||||
meshtastic_HardwareModel_T_IMPULSE_PLUS = 135,
|
||||
/* Lilygo T-Echo Card */
|
||||
meshtastic_HardwareModel_T_ECHO_CARD = 136,
|
||||
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
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.
|
||||
------------------------------------------------------------------------------------------------------------------------------------------ */
|
||||
|
||||
@@ -144,7 +144,7 @@ typedef struct _meshtastic_ModuleConfig_MQTTConfig {
|
||||
(the default official mqtt.meshtastic.org server can handle encrypted packets)
|
||||
Decrypted packets may be useful for external systems that want to consume meshtastic packets */
|
||||
bool encryption_enabled;
|
||||
/* Whether to send / consume json packets on MQTT */
|
||||
/* Deprecated: JSON packet support on MQTT was removed, and this field is ignored. */
|
||||
bool json_enabled;
|
||||
/* If true, we attempt to establish a secure connection using TLS */
|
||||
bool tls_enabled;
|
||||
|
||||
@@ -13,9 +13,8 @@
|
||||
#include <WebServer.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
|
||||
@@ -12,9 +12,8 @@
|
||||
|
||||
#include <AsyncUDP.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#define UDP_MULTICAST_DEFAUL_PORT 4403 // Default port for UDP multicast is same as TCP api server
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
#include "target_specific.h"
|
||||
#include <WiFi.h>
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_CH390D)
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
#if HAS_ETHERNET && defined(USE_WS5500)
|
||||
#include <ETHClass2.h>
|
||||
#define ETH ETH2
|
||||
#if HAS_ETHERNET && defined(ARCH_ESP32)
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
|
||||
extern bool needReconnect;
|
||||
|
||||
@@ -262,7 +262,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
break;
|
||||
}
|
||||
case meshtastic_AdminMessage_ota_request_tag: {
|
||||
#if defined(ARCH_ESP32)
|
||||
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI
|
||||
LOG_INFO("OTA Requested");
|
||||
|
||||
if (r->ota_request.ota_hash.size != 32) {
|
||||
@@ -666,7 +666,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c, bool fromOthers)
|
||||
bool requiresReboot = true;
|
||||
|
||||
switch (c.which_payload_variant) {
|
||||
case meshtastic_Config_device_tag:
|
||||
case meshtastic_Config_device_tag: {
|
||||
LOG_INFO("Set config: Device");
|
||||
config.has_device = true;
|
||||
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && \
|
||||
@@ -720,6 +720,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c, bool fromOthers)
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
} // case meshtastic_Config_device_tag
|
||||
case meshtastic_Config_position_tag:
|
||||
LOG_INFO("Set config: Position");
|
||||
config.has_position = true;
|
||||
@@ -823,7 +824,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c, bool fromOthers)
|
||||
// Ensure initRegion() uses the newly validated region
|
||||
config.lora.region = validatedLora.region;
|
||||
initRegion();
|
||||
if (myRegion->dutyCycle < 100) {
|
||||
if (getEffectiveDutyCycle() < 100) {
|
||||
validatedLora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit
|
||||
}
|
||||
if (strncmp(moduleConfig.mqtt.root, default_mqtt_root, strlen(default_mqtt_root)) == 0) {
|
||||
@@ -1012,11 +1013,11 @@ bool AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c)
|
||||
case meshtastic_ModuleConfig_neighbor_info_tag:
|
||||
LOG_INFO("Set module config: Neighbor Info");
|
||||
moduleConfig.has_neighbor_info = true;
|
||||
moduleConfig.neighbor_info = c.payload_variant.neighbor_info;
|
||||
if (moduleConfig.neighbor_info.update_interval < min_neighbor_info_broadcast_secs) {
|
||||
LOG_DEBUG("Tried to set update_interval too low, setting to %d", default_neighbor_info_broadcast_secs);
|
||||
moduleConfig.neighbor_info.update_interval = default_neighbor_info_broadcast_secs;
|
||||
}
|
||||
moduleConfig.neighbor_info = c.payload_variant.neighbor_info;
|
||||
break;
|
||||
case meshtastic_ModuleConfig_detection_sensor_tag:
|
||||
LOG_INFO("Set module config: Detection Sensor");
|
||||
@@ -1345,6 +1346,10 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
|
||||
if (config.bluetooth.enabled && nrf52Bluetooth) {
|
||||
conn.bluetooth.is_connected = nrf52Bluetooth->isConnected();
|
||||
}
|
||||
#elif defined(ARCH_NRF54L15)
|
||||
if (config.bluetooth.enabled && nrf54l15Bluetooth) {
|
||||
conn.bluetooth.is_connected = nrf54l15Bluetooth->isConnected();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
conn.has_serial = true; // No serial-less devices
|
||||
@@ -1605,6 +1610,9 @@ void disableBluetooth()
|
||||
#elif defined(ARCH_NRF52)
|
||||
if (nrf52Bluetooth)
|
||||
nrf52Bluetooth->shutdown();
|
||||
#elif defined(ARCH_NRF54L15)
|
||||
if (nrf54l15Bluetooth)
|
||||
nrf54l15Bluetooth->shutdown();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,207 +1,11 @@
|
||||
#include "AtakPluginModule.h"
|
||||
#include "Default.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "mesh/compression/unishox2.h"
|
||||
#include "meshUtils.h"
|
||||
#include "meshtastic/atak.pb.h"
|
||||
|
||||
AtakPluginModule *atakPluginModule;
|
||||
|
||||
AtakPluginModule::AtakPluginModule()
|
||||
: ProtobufModule("atak", meshtastic_PortNum_ATAK_PLUGIN, &meshtastic_TAKPacket_msg), concurrency::OSThread("AtakPlugin")
|
||||
AtakPluginModule::AtakPluginModule() : SinglePortModule("atak", meshtastic_PortNum_ATAK_PLUGIN_V2) {}
|
||||
|
||||
ProcessMessage AtakPluginModule::handleReceived(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
ourPortNum = meshtastic_PortNum_ATAK_PLUGIN;
|
||||
(void)mp; // Passthrough — no processing needed, apps handle compression/decompression
|
||||
return ProcessMessage::CONTINUE;
|
||||
}
|
||||
|
||||
/*
|
||||
Encompasses the full construction and sending packet to mesh
|
||||
Will be used for broadcast.
|
||||
*/
|
||||
int32_t AtakPluginModule::runOnce()
|
||||
{
|
||||
return default_broadcast_interval_secs;
|
||||
}
|
||||
|
||||
bool AtakPluginModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_TAKPacket *r)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
meshtastic_TAKPacket AtakPluginModule::cloneTAKPacketData(meshtastic_TAKPacket *t)
|
||||
{
|
||||
meshtastic_TAKPacket clone = meshtastic_TAKPacket_init_zero;
|
||||
if (t->has_group) {
|
||||
clone.has_group = true;
|
||||
clone.group = t->group;
|
||||
}
|
||||
if (t->has_status) {
|
||||
clone.has_status = true;
|
||||
clone.status = t->status;
|
||||
}
|
||||
if (t->has_contact) {
|
||||
clone.has_contact = true;
|
||||
clone.contact = {0};
|
||||
}
|
||||
|
||||
if (t->which_payload_variant == meshtastic_TAKPacket_pli_tag) {
|
||||
clone.which_payload_variant = meshtastic_TAKPacket_pli_tag;
|
||||
clone.payload_variant.pli = t->payload_variant.pli;
|
||||
} else if (t->which_payload_variant == meshtastic_TAKPacket_chat_tag) {
|
||||
clone.which_payload_variant = meshtastic_TAKPacket_chat_tag;
|
||||
clone.payload_variant.chat = {0};
|
||||
} else if (t->which_payload_variant == meshtastic_TAKPacket_detail_tag) {
|
||||
clone.which_payload_variant = meshtastic_TAKPacket_detail_tag;
|
||||
clone.payload_variant.detail.size = t->payload_variant.detail.size;
|
||||
memcpy(clone.payload_variant.detail.bytes, t->payload_variant.detail.bytes, t->payload_variant.detail.size);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
void AtakPluginModule::alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic_TAKPacket *t)
|
||||
{
|
||||
// From Phone (EUD)
|
||||
if (mp.from == 0) {
|
||||
LOG_DEBUG("Received uncompressed TAK payload from phone: %d bytes", mp.decoded.payload.size);
|
||||
// Compress for LoRA transport
|
||||
auto compressed = cloneTAKPacketData(t);
|
||||
compressed.is_compressed = true;
|
||||
if (t->has_contact) {
|
||||
auto length = unishox2_compress_lines(
|
||||
t->contact.callsign, pb_string_length(t->contact.callsign, sizeof(t->contact.callsign)),
|
||||
compressed.contact.callsign, sizeof(compressed.contact.callsign) - 1, USX_PSET_DFLT, NULL);
|
||||
if (length < 0) {
|
||||
LOG_WARN("Compress overflow contact.callsign. Revert to uncompressed packet");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Compressed callsign: %d bytes", length);
|
||||
length = unishox2_compress_lines(
|
||||
t->contact.device_callsign, pb_string_length(t->contact.device_callsign, sizeof(t->contact.device_callsign)),
|
||||
compressed.contact.device_callsign, sizeof(compressed.contact.device_callsign) - 1, USX_PSET_DFLT, NULL);
|
||||
if (length < 0) {
|
||||
LOG_WARN("Compress overflow contact.device_callsign. Revert to uncompressed packet");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Compressed device_callsign: %d bytes", length);
|
||||
}
|
||||
if (t->which_payload_variant == meshtastic_TAKPacket_chat_tag) {
|
||||
auto length = unishox2_compress_lines(
|
||||
t->payload_variant.chat.message,
|
||||
pb_string_length(t->payload_variant.chat.message, sizeof(t->payload_variant.chat.message)),
|
||||
compressed.payload_variant.chat.message, sizeof(compressed.payload_variant.chat.message) - 1, USX_PSET_DFLT,
|
||||
NULL);
|
||||
if (length < 0) {
|
||||
LOG_WARN("Compress overflow chat.message. Revert to uncompressed packet");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Compressed chat message: %d bytes", length);
|
||||
|
||||
if (t->payload_variant.chat.has_to) {
|
||||
compressed.payload_variant.chat.has_to = true;
|
||||
length = unishox2_compress_lines(
|
||||
t->payload_variant.chat.to, pb_string_length(t->payload_variant.chat.to, sizeof(t->payload_variant.chat.to)),
|
||||
compressed.payload_variant.chat.to, sizeof(compressed.payload_variant.chat.to) - 1, USX_PSET_DFLT, NULL);
|
||||
if (length < 0) {
|
||||
LOG_WARN("Compress overflow chat.to. Revert to uncompressed packet");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Compressed chat to: %d bytes", length);
|
||||
}
|
||||
|
||||
if (t->payload_variant.chat.has_to_callsign) {
|
||||
compressed.payload_variant.chat.has_to_callsign = true;
|
||||
length = unishox2_compress_lines(
|
||||
t->payload_variant.chat.to_callsign,
|
||||
pb_string_length(t->payload_variant.chat.to_callsign, sizeof(t->payload_variant.chat.to_callsign)),
|
||||
compressed.payload_variant.chat.to_callsign, sizeof(compressed.payload_variant.chat.to_callsign) - 1,
|
||||
USX_PSET_DFLT, NULL);
|
||||
if (length < 0) {
|
||||
LOG_WARN("Compress overflow chat.to_callsign. Revert to uncompressed packet");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Compressed chat to_callsign: %d bytes", length);
|
||||
}
|
||||
}
|
||||
mp.decoded.payload.size = pb_encode_to_bytes(mp.decoded.payload.bytes, sizeof(mp.decoded.payload.bytes),
|
||||
meshtastic_TAKPacket_fields, &compressed);
|
||||
LOG_DEBUG("Final payload: %d bytes", mp.decoded.payload.size);
|
||||
} else {
|
||||
if (!t->is_compressed) {
|
||||
// Not compressed. Something is wrong
|
||||
LOG_WARN("Received uncompressed TAKPacket over radio! Skip");
|
||||
return;
|
||||
}
|
||||
|
||||
// Decompress for Phone (EUD)
|
||||
auto uncompressed = cloneTAKPacketData(t);
|
||||
uncompressed.is_compressed = false;
|
||||
if (t->has_contact) {
|
||||
auto length = unishox2_decompress_lines(
|
||||
t->contact.callsign, pb_string_length(t->contact.callsign, sizeof(t->contact.callsign)),
|
||||
uncompressed.contact.callsign, sizeof(uncompressed.contact.callsign) - 1, USX_PSET_DFLT, NULL);
|
||||
if (length < 0) {
|
||||
LOG_WARN("Decompress overflow contact.callsign. Bailing out");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Decompressed callsign: %d bytes", length);
|
||||
|
||||
length = unishox2_decompress_lines(
|
||||
t->contact.device_callsign, pb_string_length(t->contact.device_callsign, sizeof(t->contact.device_callsign)),
|
||||
uncompressed.contact.device_callsign, sizeof(uncompressed.contact.device_callsign) - 1, USX_PSET_DFLT, NULL);
|
||||
if (length < 0) {
|
||||
LOG_WARN("Decompress overflow contact.device_callsign. Bailing out");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Decompressed device_callsign: %d bytes", length);
|
||||
}
|
||||
if (uncompressed.which_payload_variant == meshtastic_TAKPacket_chat_tag) {
|
||||
auto length = unishox2_decompress_lines(
|
||||
t->payload_variant.chat.message,
|
||||
pb_string_length(t->payload_variant.chat.message, sizeof(t->payload_variant.chat.message)),
|
||||
uncompressed.payload_variant.chat.message, sizeof(uncompressed.payload_variant.chat.message) - 1, USX_PSET_DFLT,
|
||||
NULL);
|
||||
if (length < 0) {
|
||||
LOG_WARN("Decompress overflow chat.message. Bailing out");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Decompressed chat message: %d bytes", length);
|
||||
|
||||
if (t->payload_variant.chat.has_to) {
|
||||
uncompressed.payload_variant.chat.has_to = true;
|
||||
length = unishox2_decompress_lines(
|
||||
t->payload_variant.chat.to, pb_string_length(t->payload_variant.chat.to, sizeof(t->payload_variant.chat.to)),
|
||||
uncompressed.payload_variant.chat.to, sizeof(uncompressed.payload_variant.chat.to) - 1, USX_PSET_DFLT, NULL);
|
||||
if (length < 0) {
|
||||
LOG_WARN("Decompress overflow chat.to. Bailing out");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Decompressed chat to: %d bytes", length);
|
||||
}
|
||||
|
||||
if (t->payload_variant.chat.has_to_callsign) {
|
||||
uncompressed.payload_variant.chat.has_to_callsign = true;
|
||||
length = unishox2_decompress_lines(
|
||||
t->payload_variant.chat.to_callsign,
|
||||
pb_string_length(t->payload_variant.chat.to_callsign, sizeof(t->payload_variant.chat.to_callsign)),
|
||||
uncompressed.payload_variant.chat.to_callsign, sizeof(uncompressed.payload_variant.chat.to_callsign) - 1,
|
||||
USX_PSET_DFLT, NULL);
|
||||
if (length < 0) {
|
||||
LOG_WARN("Decompress overflow chat.to_callsign. Bailing out");
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Decompressed chat to_callsign: %d bytes", length);
|
||||
}
|
||||
}
|
||||
auto decompressedCopy = packetPool.allocCopy(mp);
|
||||
decompressedCopy->decoded.payload.size =
|
||||
pb_encode_to_bytes(decompressedCopy->decoded.payload.bytes, sizeof(decompressedCopy->decoded.payload),
|
||||
meshtastic_TAKPacket_fields, &uncompressed);
|
||||
|
||||
service->sendToPhone(decompressedCopy);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
#pragma once
|
||||
#include "ProtobufModule.h"
|
||||
#include "meshtastic/atak.pb.h"
|
||||
#include "SinglePortModule.h"
|
||||
|
||||
/**
|
||||
* Waypoint message handling for meshtastic
|
||||
* ATAK Plugin V2 module - passthrough for ATAK_PLUGIN_V2 payloads.
|
||||
* The wire format includes a leading flags byte followed by opaque payload bytes.
|
||||
* Depending on the flags, the payload may be zstd dictionary-compressed or raw/uncompressed protobuf.
|
||||
* Compression/decompression and payload interpretation are handled by the apps
|
||||
* (Android, iOS, ATAK plugin); firmware forwards the bytes unchanged on the
|
||||
* ATAK_PLUGIN_V2 port.
|
||||
*/
|
||||
class AtakPluginModule : public ProtobufModule<meshtastic_TAKPacket>, private concurrency::OSThread
|
||||
class AtakPluginModule : public SinglePortModule
|
||||
{
|
||||
public:
|
||||
/** Constructor
|
||||
@@ -14,13 +18,7 @@ class AtakPluginModule : public ProtobufModule<meshtastic_TAKPacket>, private co
|
||||
AtakPluginModule();
|
||||
|
||||
protected:
|
||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_TAKPacket *t) override;
|
||||
virtual void alterReceivedProtobuf(meshtastic_MeshPacket &mp, meshtastic_TAKPacket *t) override;
|
||||
/* Does our periodic broadcast */
|
||||
int32_t runOnce() override;
|
||||
|
||||
private:
|
||||
meshtastic_TAKPacket cloneTAKPacketData(meshtastic_TAKPacket *t);
|
||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
||||
};
|
||||
|
||||
extern AtakPluginModule *atakPluginModule;
|
||||
extern AtakPluginModule *atakPluginModule;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "CannedMessageModule.h"
|
||||
#include "Channels.h"
|
||||
#include "FSCommon.h"
|
||||
#include "MeshRadio.h"
|
||||
#include "MeshService.h"
|
||||
#include "MessageStore.h"
|
||||
#include "NodeDB.h"
|
||||
@@ -2103,16 +2104,16 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
static float getSnrLimit(meshtastic_Config_LoRaConfig_ModemPreset preset)
|
||||
{
|
||||
switch (preset) {
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
|
||||
case PRESET(LONG_SLOW):
|
||||
case PRESET(LONG_MODERATE):
|
||||
case PRESET(LONG_FAST):
|
||||
return -6.0f;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
|
||||
case PRESET(MEDIUM_SLOW):
|
||||
case PRESET(MEDIUM_FAST):
|
||||
return -5.5f;
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
|
||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
|
||||
case PRESET(SHORT_SLOW):
|
||||
case PRESET(SHORT_FAST):
|
||||
case PRESET(SHORT_TURBO):
|
||||
return -4.5f;
|
||||
default:
|
||||
return -6.0f;
|
||||
|
||||
@@ -206,6 +206,10 @@ void ExternalNotificationModule::setExternalState(uint8_t index, bool on)
|
||||
#ifdef PCA_LED_NOTIFICATION
|
||||
io.digitalWrite(PCA_LED_NOTIFICATION, on);
|
||||
|
||||
#endif
|
||||
#ifdef NEOPIXEL_STATUS_NOTIFICATION_PIN
|
||||
notificationPixel.setPixelColor(0, on ? NEOPIXEL_STATUS_NOTIFICATION_COLOR : 0);
|
||||
notificationPixel.show();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -324,6 +328,12 @@ ExternalNotificationModule::ExternalNotificationModule()
|
||||
LOG_INFO("Use Pin %i in digital mode", output);
|
||||
pinMode(output, OUTPUT);
|
||||
}
|
||||
#ifdef NEOPIXEL_STATUS_NOTIFICATION_PIN
|
||||
LOG_INFO("Use WS2812 on GPIO %d as notification LED", NEOPIXEL_STATUS_NOTIFICATION_PIN);
|
||||
notificationPixel.begin();
|
||||
notificationPixel.clear();
|
||||
notificationPixel.show();
|
||||
#endif
|
||||
setExternalState(0, false);
|
||||
externalTurnedOn[0] = 0;
|
||||
if (moduleConfig.external_notification.output_vibra) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user