Merge branch 'develop' into clear-rangetest-results

This commit is contained in:
Ford Jones
2025-09-09 12:05:00 +12:00
committed by GitHub
119 changed files with 2197 additions and 909 deletions
+2 -2
View File
@@ -76,7 +76,7 @@ bool loopCanSleep()
// Called just prior to starting Meshtastic. Allows for setting config values before startup. // Called just prior to starting Meshtastic. Allows for setting config values before startup.
void lateInitVariant() void lateInitVariant()
{ {
settingsMap[logoutputlevel] = level_error; portduino_config.logoutputlevel = level_error;
channelFile.channels[0] = meshtastic_Channel{ channelFile.channels[0] = meshtastic_Channel{
.has_settings = true, .has_settings = true,
.settings = .settings =
@@ -132,7 +132,7 @@ int portduino_main(int argc, char **argv); // Renamed "main" function from Mesht
// Start Meshtastic in a thread and wait till it has reached the ON state. // Start Meshtastic in a thread and wait till it has reached the ON state.
int LLVMFuzzerInitialize(int *argc, char ***argv) int LLVMFuzzerInitialize(int *argc, char ***argv)
{ {
settingsMap[maxtophone] = 5; portduino_config.maxtophone = 5;
meshtasticThread = std::thread([program = *argv[0]]() { meshtasticThread = std::thread([program = *argv[0]]() {
char nodeIdStr[12]; char nodeIdStr[12];
+1 -1
View File
@@ -23,7 +23,7 @@ runs:
sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev lsb-release sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev lsb-release
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
cache: pip cache: pip
+8 -4
View File
@@ -43,11 +43,15 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
- uses: actions/setup-python@v5 - uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
cache: pip cache: pip
- run: pip install -U platformio - run: pip install -U platformio
- name: Uncomment build epoch
shell: bash
run: |
sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
- name: Generate matrix - name: Generate matrix
id: jsonStep id: jsonStep
run: | run: |
@@ -370,7 +374,7 @@ jobs:
uses: actions/checkout@v5 uses: actions/checkout@v5
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
@@ -439,7 +443,7 @@ jobs:
uses: actions/checkout@v5 uses: actions/checkout@v5
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
@@ -494,7 +498,7 @@ jobs:
uses: actions/checkout@v5 uses: actions/checkout@v5
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
+1 -1
View File
@@ -31,7 +31,7 @@ jobs:
repository: ${{github.event.pull_request.head.repo.full_name}} repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- name: Check for PR labels - name: Check for PR labels
uses: actions/github-script@v7 uses: actions/github-script@v8
with: with:
script: | script: |
const labels = context.payload.pull_request.labels.map(label => label.name); const labels = context.payload.pull_request.labels.map(label => label.name);
+1 -1
View File
@@ -177,7 +177,7 @@ jobs:
- name: Comment test results on PR - name: Comment test results on PR
if: github.event_name == 'pull_request' && needs.native-tests.result != 'skipped' if: github.event_name == 'pull_request' && needs.native-tests.result != 'skipped'
uses: actions/github-script@v7 uses: actions/github-script@v8
with: with:
script: | script: |
const fs = require('fs'); const fs = require('fs');
+1 -1
View File
@@ -63,7 +63,7 @@ jobs:
uses: actions/checkout@v5 uses: actions/checkout@v5
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v5 uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
steps: steps:
- name: Stale PR+Issues - name: Stale PR+Issues
uses: actions/stale@v9.1.0 uses: actions/stale@v10.0.0
with: with:
days-before-stale: 45 days-before-stale: 45
exempt-issue-labels: pinned,3.0 exempt-issue-labels: pinned,3.0
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
pio upgrade pio upgrade
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v5
with: with:
node-version: 22 node-version: 22
+1 -1
View File
@@ -39,7 +39,7 @@ jobs:
git push git push
- name: Comment on PR - name: Comment on PR
uses: actions/github-script@v7 uses: actions/github-script@v8
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
script: | script: |
+4 -4
View File
@@ -8,13 +8,13 @@ plugins:
uri: https://github.com/trunk-io/plugins uri: https://github.com/trunk-io/plugins
lint: lint:
enabled: enabled:
- checkov@3.2.467 - checkov@3.2.469
- renovate@41.90.1 - renovate@41.94.0
- prettier@3.6.2 - prettier@3.6.2
- trufflehog@3.90.5 - trufflehog@3.90.5
- yamllint@1.37.1 - yamllint@1.37.1
- bandit@1.8.6 - bandit@1.8.6
- trivy@0.65.0 - trivy@0.66.0
- taplo@0.10.0 - taplo@0.10.0
- ruff@0.12.11 - ruff@0.12.11
- isort@6.0.1 - isort@6.0.1
@@ -23,7 +23,7 @@ lint:
- svgo@4.0.0 - svgo@4.0.0
- actionlint@1.7.7 - actionlint@1.7.7
- flake8@7.3.0 - flake8@7.3.0
- hadolint@2.12.1-beta - hadolint@2.13.1
- shfmt@3.6.0 - shfmt@3.6.0
- shellcheck@0.11.0 - shellcheck@0.11.0
- black@25.1.0 - black@25.1.0
+3 -1
View File
@@ -2,7 +2,7 @@
[portduino_base] [portduino_base]
platform = platform =
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop # renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
https://github.com/meshtastic/platform-native/archive/37d986499ce24511952d7146db72d667c6bdaff7.zip https://github.com/meshtastic/platform-native/archive/c490bcd019e0658404088a61b96e653c9da22c45.zip
framework = arduino framework = arduino
build_src_filter = build_src_filter =
@@ -31,6 +31,8 @@ lib_deps =
https://github.com/pine64/libch341-spi-userspace/archive/af9bc27c9c30fa90772279925b7c5913dff789b4.zip https://github.com/pine64/libch341-spi-userspace/archive/af9bc27c9c30fa90772279925b7c5913dff789b4.zip
# renovate: datasource=custom.pio depName=adafruit/Adafruit seesaw Library packageName=adafruit/library/Adafruit seesaw Library # renovate: datasource=custom.pio depName=adafruit/Adafruit seesaw Library packageName=adafruit/library/Adafruit seesaw Library
adafruit/Adafruit seesaw Library@1.7.9 adafruit/Adafruit seesaw Library@1.7.9
# renovate: datasource=git-refs depName=RAK12034-BMX160 packageName=https://github.com/RAKWireless/RAK12034-BMX160 gitBranch=main
https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip
build_flags = build_flags =
${arduino_base.build_flags} ${arduino_base.build_flags}
+23 -6
View File
@@ -7,6 +7,7 @@ SET "DEBUG=0"
SET "PYTHON=" SET "PYTHON="
SET "TFT_BUILD=0" SET "TFT_BUILD=0"
SET "BIGDB8=0" SET "BIGDB8=0"
SET "MUIDB8=0"
SET "BIGDB16=0" SET "BIGDB16=0"
SET "ESPTOOL_BAUD=115200" SET "ESPTOOL_BAUD=115200"
SET "ESPTOOL_CMD=" SET "ESPTOOL_CMD="
@@ -14,11 +15,12 @@ SET "LOGCOUNTER=0"
SET "BPS_RESET=0" SET "BPS_RESET=0"
@REM FIXME: Determine mcu from PlatformIO variant, this is unmaintainable. @REM FIXME: Determine mcu from PlatformIO variant, this is unmaintainable.
SET "S3=s3 v3 t-deck wireless-paper wireless-tracker station-g2 unphone" SET "S3=s3 v3 t-deck wireless-paper wireless-tracker station-g2 unphone t-eth-elite tlora-pager mesh-tab dreamcatcher ESP32-S3-Pico seeed-sensecap-indicator heltec_capsule_sensor_v3 vision-master icarus tracksenger elecrow-adv"
SET "C3=esp32c3" SET "C3=esp32c3"
@REM FIXME: Determine flash size from PlatformIO variant, this is unmaintainable. @REM FIXME: Determine flash size from PlatformIO variant, this is unmaintainable.
SET "BIGDB_8MB=picomputer-s3 unphone seeed-sensecap-indicator crowpanel-esp32s3 heltec_capsule_sensor_v3 heltec-v3 heltec-vision-master-e213 heltec-vision-master-e290 heltec-vision-master-t190 heltec-wireless-paper heltec-wireless-tracker heltec-wsl-v3 icarus seeed-xiao-s3 tbeam-s3-core tracksenger" SET "BIGDB_8MB=crowpanel-esp32s3 heltec_capsule_sensor_v3 heltec-v3 heltec-vision-master-e213 heltec-vision-master-e290 heltec-vision-master-t190 heltec-wireless-paper heltec-wireless-tracker heltec-wsl-v3 icarus seeed-xiao-s3 tbeam-s3-core tracksenger"
SET "BIGDB_16MB=t-deck mesh-tab t-energy-s3 dreamcatcher ESP32-S3-Pico m5stack-cores3 station-g2 t-eth-elite t-watch-s3" SET "MUIDB_8MB=picomputer-s3 unphone seeed-sensecap-indicator"
SET "BIGDB_16MB=t-deck mesh-tab t-energy-s3 dreamcatcher ESP32-S3-Pico m5stack-cores3 station-g2 t-eth-elite tlora-pager t-watch-s3 elecrow-adv"
GOTO getopts GOTO getopts
:help :help
@@ -119,11 +121,10 @@ IF NOT "__%PYTHON%__"=="____" (
CALL :LOG_MESSAGE DEBUG "Checking esptool command !ESPTOOL_CMD!..." CALL :LOG_MESSAGE DEBUG "Checking esptool command !ESPTOOL_CMD!..."
!ESPTOOL_CMD! >nul 2>&1 !ESPTOOL_CMD! >nul 2>&1
IF %ERRORLEVEL% GEQ 2 ( IF %ERRORLEVEL% EQU 9009 (
@REM esptool exits with code 1 if help is displayed. @REM 9009 = command not found on Windows
CALL :LOG_MESSAGE ERROR "esptool not found: !ESPTOOL_CMD!" CALL :LOG_MESSAGE ERROR "esptool not found: !ESPTOOL_CMD!"
EXIT /B 1 EXIT /B 1
GOTO eof
) )
IF %DEBUG% EQU 1 ( IF %DEBUG% EQU 1 (
CALL :LOG_MESSAGE DEBUG "Skipping ESPTOOL_CMD steps." CALL :LOG_MESSAGE DEBUG "Skipping ESPTOOL_CMD steps."
@@ -163,6 +164,15 @@ FOR %%a IN (%BIGDB_8MB%) DO (
) )
:end_loop_bigdb_8mb :end_loop_bigdb_8mb
FOR %%a IN (%MUIDB_8MB%) DO (
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
@REM We are working with any of %MUIDB_8MB%.
SET "MUIDB8=1"
GOTO end_loop_muidb_8mb
)
)
:end_loop_muidb_8mb
FOR %%a IN (%BIGDB_16MB%) DO ( FOR %%a IN (%BIGDB_16MB%) DO (
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" ( IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
@REM We are working with any of %BIGDB_16MB%. @REM We are working with any of %BIGDB_16MB%.
@@ -173,6 +183,7 @@ FOR %%a IN (%BIGDB_16MB%) DO (
:end_loop_bigdb_16mb :end_loop_bigdb_16mb
IF %BIGDB8% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 8mb partition selected." IF %BIGDB8% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 8mb partition selected."
IF %MUIDB8% EQU 1 CALL :LOG_MESSAGE INFO "MUIDB 8mb partition selected."
IF %BIGDB16% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 16mb partition selected." IF %BIGDB16% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 16mb partition selected."
@REM Extract BASENAME from %FILENAME% for later use. @REM Extract BASENAME from %FILENAME% for later use.
@@ -217,6 +228,12 @@ IF %BIGDB8% EQU 1 (
SET "SPIFFS_OFFSET=0x670000" SET "SPIFFS_OFFSET=0x670000"
) )
@REM Offsets for MUIDB 8mb.
IF %MUIDB8% EQU 1 (
SET "OTA_OFFSET=0x5D0000"
SET "SPIFFS_OFFSET=0x670000"
)
@REM Offsets for BigDB 16mb. @REM Offsets for BigDB 16mb.
IF %BIGDB16% EQU 1 ( IF %BIGDB16% EQU 1 (
SET "OTA_OFFSET=0x650000" SET "OTA_OFFSET=0x650000"
+44 -31
View File
@@ -5,38 +5,43 @@ BPS_RESET=false
TFT_BUILD=false TFT_BUILD=false
MCU="" MCU=""
# Constants
RESET_BAUD=1200
FIRMWARE_OFFSET=0x00
# Variant groups # Variant groups
BIGDB_8MB=( BIGDB_8MB=(
"picomputer-s3" "crowpanel-esp32s3"
"unphone" "heltec_capsule_sensor_v3"
"seeed-sensecap-indicator" "heltec-v3"
"crowpanel-esp32s3" "heltec-vision-master-e213"
"heltec_capsule_sensor_v3" "heltec-vision-master-e290"
"heltec-v3" "heltec-vision-master-t190"
"heltec-vision-master-e213" "heltec-wireless-paper"
"heltec-vision-master-e290" "heltec-wireless-tracker"
"heltec-vision-master-t190" "heltec-wsl-v3"
"heltec-wireless-paper" "icarus"
"heltec-wireless-tracker" "seeed-xiao-s3"
"heltec-wsl-v3" "tbeam-s3-core"
"icarus" "tracksenger"
"seeed-xiao-s3" )
"tbeam-s3-core" MUIDB_8MB=(
"tracksenger" "picomputer-s3"
"unphone"
"seeed-sensecap-indicator"
) )
BIGDB_16MB=( BIGDB_16MB=(
"t-deck" "t-deck"
"mesh-tab" "mesh-tab"
"t-energy-s3" "t-energy-s3"
"dreamcatcher" "dreamcatcher"
"ESP32-S3-Pico" "ESP32-S3-Pico"
"m5stack-cores3" "m5stack-cores3"
"station-g2" "station-g2"
"t-eth-elite" "t-eth-elite"
"tlora-pager"
"t-watch-s3" "t-watch-s3"
"elecrow-adv-35-tft" "elecrow-adv"
"elecrow-adv-24-28-tft"
"elecrow-adv1-43-50-70-tft"
) )
S3_VARIANTS=( S3_VARIANTS=(
"s3" "s3"
@@ -47,6 +52,7 @@ S3_VARIANTS=(
"station-g2" "station-g2"
"unphone" "unphone"
"t-eth-elite" "t-eth-elite"
"tlora-pager"
"mesh-tab" "mesh-tab"
"dreamcatcher" "dreamcatcher"
"ESP32-S3-Pico" "ESP32-S3-Pico"
@@ -106,8 +112,8 @@ while [ $# -gt 0 ]; do
shift shift
;; ;;
--1200bps-reset) --1200bps-reset)
BPS_RESET=true BPS_RESET=true
;; ;;
--) # Stop parsing options --) # Stop parsing options
shift shift
break break
@@ -121,7 +127,7 @@ while [ $# -gt 0 ]; do
done done
if [[ $BPS_RESET == true ]]; then if [[ $BPS_RESET == true ]]; then
$ESPTOOL_CMD --baud 1200 --after no_reset read_flash_status $ESPTOOL_CMD --baud $RESET_BAUD --after no_reset read_flash_status
exit 0 exit 0
fi fi
@@ -158,6 +164,13 @@ if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
fi fi
done done
for variant in "${MUIDB_8MB[@]}"; do
if [ -z "${FILENAME##*"$variant"*}" ]; then
OFFSET=0x670000
OTA_OFFSET=0x5D0000
fi
done
# littlefs* offset for BigDB 16mb and OTA OFFSET. # littlefs* offset for BigDB 16mb and OTA OFFSET.
for variant in "${BIGDB_16MB[@]}"; do for variant in "${BIGDB_16MB[@]}"; do
if [ -z "${FILENAME##*"$variant"*}" ]; then if [ -z "${FILENAME##*"$variant"*}" ]; then
@@ -201,8 +214,8 @@ if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
fi fi
echo "Trying to flash ${FILENAME}, but first erasing and writing system information" echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
$ESPTOOL_CMD erase_flash $ESPTOOL_CMD erase-flash
$ESPTOOL_CMD write_flash 0x00 "${FILENAME}" $ESPTOOL_CMD write-flash $FIRMWARE_OFFSET "${FILENAME}"
echo "Trying to flash ${OTAFILE} at offset ${OTA_OFFSET}" echo "Trying to flash ${OTAFILE} at offset ${OTA_OFFSET}"
$ESPTOOL_CMD write_flash $OTA_OFFSET "${OTAFILE}" $ESPTOOL_CMD write_flash $OTA_OFFSET "${OTAFILE}"
echo "Trying to flash ${SPIFFSFILE}, at offset ${OFFSET}" echo "Trying to flash ${SPIFFSFILE}, at offset ${OFFSET}"
+12 -11
View File
@@ -6,6 +6,8 @@ SET "SCRIPT_NAME=%~nx0"
SET "DEBUG=0" SET "DEBUG=0"
SET "PYTHON=" SET "PYTHON="
SET "ESPTOOL_BAUD=115200" SET "ESPTOOL_BAUD=115200"
SET "RESET_BAUD=1200"
SET "UPDATE_OFFSET=0x10000"
SET "ESPTOOL_CMD=" SET "ESPTOOL_CMD="
SET "LOGCOUNTER=0" SET "LOGCOUNTER=0"
SET "CHANGE_MODE=0" SET "CHANGE_MODE=0"
@@ -85,14 +87,13 @@ IF "!FILENAME:update=!"=="!FILENAME!" (
) )
:skip-filename :skip-filename
SET "ESPTOOL_BAUD=1200"
CALL :LOG_MESSAGE DEBUG "Determine the correct esptool command to use..." CALL :LOG_MESSAGE DEBUG "Determine the correct esptool command to use..."
IF NOT "__%PYTHON%__"=="____" ( IF NOT "__%PYTHON%__"=="____" (
SET "ESPTOOL_CMD=!PYTHON! -m esptool" SET "ESPTOOL_CMD=""!PYTHON!"" -m esptool"
CALL :LOG_MESSAGE DEBUG "Python interpreter supplied." CALL :LOG_MESSAGE DEBUG "Python interpreter supplied."
) ELSE ( ) ELSE (
CALL :LOG_MESSAGE DEBUG "Python interpreter NOT supplied. Looking for esptool... CALL :LOG_MESSAGE DEBUG "Python interpreter NOT supplied. Looking for esptool..."
WHERE esptool >nul 2>&1 WHERE esptool >nul 2>&1
IF %ERRORLEVEL% EQU 0 ( IF %ERRORLEVEL% EQU 0 (
@REM WHERE exits with code 0 if esptool is found. @REM WHERE exits with code 0 if esptool is found.
@@ -105,11 +106,11 @@ IF NOT "__%PYTHON%__"=="____" (
CALL :LOG_MESSAGE DEBUG "Checking esptool command !ESPTOOL_CMD!..." CALL :LOG_MESSAGE DEBUG "Checking esptool command !ESPTOOL_CMD!..."
!ESPTOOL_CMD! >nul 2>&1 !ESPTOOL_CMD! >nul 2>&1
IF %ERRORLEVEL% GEQ 2 ( CALL :LOG_MESSAGE DEBUG "esptool exit code: %ERRORLEVEL%"
@REM esptool exits with code 1 if help is displayed. IF %ERRORLEVEL% EQU 9009 (
@REM 9009 = command not found on Windows
CALL :LOG_MESSAGE ERROR "esptool not found: !ESPTOOL_CMD!" CALL :LOG_MESSAGE ERROR "esptool not found: !ESPTOOL_CMD!"
EXIT /B 1 EXIT /B 1
GOTO eof
) )
IF %DEBUG% EQU 1 ( IF %DEBUG% EQU 1 (
CALL :LOG_MESSAGE DEBUG "Skipping ESPTOOL_CMD steps." CALL :LOG_MESSAGE DEBUG "Skipping ESPTOOL_CMD steps."
@@ -127,13 +128,13 @@ CALL :LOG_MESSAGE INFO "Using esptool baud: !ESPTOOL_BAUD!."
IF %CHANGE_MODE% EQU 1 ( IF %CHANGE_MODE% EQU 1 (
@REM Attempt to change mode via 1200bps Reset. @REM Attempt to change mode via 1200bps Reset.
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! --after no_reset read_flash_status CALL :RUN_ESPTOOL !RESET_BAUD! --after no_reset read_flash_status
GOTO eof GOTO eof
) )
@REM Flashing operations. @REM Flashing operations.
CALL :LOG_MESSAGE INFO "Trying to flash update "!FILENAME!" at OFFSET 0x10000..." CALL :LOG_MESSAGE INFO "Trying to flash update "!FILENAME!" at OFFSET !UPDATE_OFFSET!..."
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write_flash 0x10000 "!FILENAME!" || GOTO eof CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write-flash !UPDATE_OFFSET! "!FILENAME!" || GOTO eof
CALL :LOG_MESSAGE INFO "Script complete!." CALL :LOG_MESSAGE INFO "Script complete!."
@@ -145,9 +146,9 @@ EXIT /B %ERRORLEVEL%
:RUN_ESPTOOL :RUN_ESPTOOL
@REM Subroutine used to run ESPTOOL_CMD with arguments. @REM Subroutine used to run ESPTOOL_CMD with arguments.
@REM Also handles %ERRORLEVEL%. @REM Also handles %ERRORLEVEL%.
@REM CALL :RUN_ESPTOOL [Baud] [erase_flash|write_flash] [OFFSET] [Filename] @REM CALL :RUN_ESPTOOL [Baud] [erase-flash|write-flash] [OFFSET] [Filename]
@REM. @REM.
@REM Example:: CALL :RUN_ESPTOOL 115200 write_flash 0x10000 "firmwarefile.bin" @REM Example:: CALL :RUN_ESPTOOL 115200 write-flash 0x10000 "firmwarefile.bin"
IF %DEBUG% EQU 1 CALL :LOG_MESSAGE DEBUG "About to run command: !ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4" IF %DEBUG% EQU 1 CALL :LOG_MESSAGE DEBUG "About to run command: !ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4"
CALL :RESET_ERROR CALL :RESET_ERROR
!ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4 !ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4
+7 -2
View File
@@ -3,6 +3,11 @@
PYTHON=${PYTHON:-$(which python3 python|head -n 1)} PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
CHANGE_MODE=false CHANGE_MODE=false
# Constants
FLASH_BAUD=115200
RESET_BAUD=1200
UPDATE_OFFSET=0x10000
# Determine the correct esptool command to use # Determine the correct esptool command to use
if "$PYTHON" -m esptool version >/dev/null 2>&1; then if "$PYTHON" -m esptool version >/dev/null 2>&1; then
ESPTOOL_CMD="$PYTHON -m esptool" ESPTOOL_CMD="$PYTHON -m esptool"
@@ -64,7 +69,7 @@ done
shift "$((OPTIND-1))" shift "$((OPTIND-1))"
if [ "$CHANGE_MODE" = true ]; then if [ "$CHANGE_MODE" = true ]; then
$ESPTOOL_CMD --baud 1200 --after no_reset read_flash_status $ESPTOOL_CMD --baud $RESET_BAUD --after no_reset read_flash_status
exit 0 exit 0
fi fi
@@ -75,7 +80,7 @@ fi
if [ -f "${FILENAME}" ] && [ -z "${FILENAME##*"update"*}" ]; then if [ -f "${FILENAME}" ] && [ -z "${FILENAME##*"update"*}" ]; then
echo "Trying to flash update ${FILENAME}" echo "Trying to flash update ${FILENAME}"
$ESPTOOL_CMD --baud 115200 write_flash 0x10000 "${FILENAME}" $ESPTOOL_CMD --baud $FLASH_BAUD write-flash $UPDATE_OFFSET "${FILENAME}"
else else
show_help show_help
echo "Invalid file: ${FILENAME}" echo "Invalid file: ${FILENAME}"
@@ -87,6 +87,9 @@
</screenshots> </screenshots>
<releases> <releases>
<release version="2.7.9" date="2025-09-03">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.9</url>
</release>
<release version="2.7.8" date="2025-08-30"> <release version="2.7.8" date="2025-08-30">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.8</url> <url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.8</url>
</release> </release>
+1 -1
View File
@@ -2,7 +2,7 @@
"build": { "build": {
"arduino": { "arduino": {
"ldscript": "esp32s3_out.ld", "ldscript": "esp32s3_out.ld",
"partitions": "default_8MB.csv", "partitions": "partition-table-8MB.csv",
"memory_type": "qio_opi" "memory_type": "qio_opi"
}, },
"core": "esp32", "core": "esp32",
+1 -1
View File
@@ -3,7 +3,7 @@
"arduino": { "arduino": {
"ldscript": "esp32s3_out.ld", "ldscript": "esp32s3_out.ld",
"memory_type": "qio_opi", "memory_type": "qio_opi",
"partitions": "default_8MB.csv" "partitions": "partition-table-8MB.csv"
}, },
"core": "esp32", "core": "esp32",
"extra_flags": [ "extra_flags": [
+5 -2
View File
@@ -1,4 +1,4 @@
meshtasticd (2.7.8.0) UNRELEASED; urgency=medium meshtasticd (2.7.9.0) UNRELEASED; urgency=medium
[ Austin Lane ] [ Austin Lane ]
* Initial packaging * Initial packaging
@@ -44,4 +44,7 @@ meshtasticd (2.7.8.0) UNRELEASED; urgency=medium
[ ] [ ]
* GitHub Actions Automatic version bump * GitHub Actions Automatic version bump
-- <github-actions[bot]@users.noreply.github.com> Sat, 30 Aug 2025 00:26:04 +0000 [ ]
* GitHub Actions Automatic version bump
-- <github-actions[bot]@users.noreply.github.com> Wed, 03 Sep 2025 23:39:17 +0000
+7
View File
@@ -0,0 +1,7 @@
# This is a layout for 8MB of flash for MUI devices
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x5C0000,
flashApp, app, ota_1, 0x5D0000,0x0A0000,
spiffs, data, spiffs, 0x670000,0x180000
1 # This is a layout for 8MB of flash for MUI devices
2 # Name, Type, SubType, Offset, Size, Flags
3 nvs, data, nvs, 0x9000, 0x5000,
4 otadata, data, ota, 0xe000, 0x2000,
5 app0, app, ota_0, 0x10000, 0x5C0000,
6 flashApp, app, ota_1, 0x5D0000,0x0A0000,
7 spiffs, data, spiffs, 0x670000,0x180000
+5 -3
View File
@@ -118,7 +118,7 @@ lib_deps =
[device-ui_base] [device-ui_base]
lib_deps = lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/a3e0e1be372d069f47b4c19d718f5267251744d7.zip https://github.com/meshtastic/device-ui/archive/233d18ef42e9d189f90fdfe621f0cd7edff2d221.zip
; Common libs for environmental measurements in telemetry module ; Common libs for environmental measurements in telemetry module
[environmental_base] [environmental_base]
@@ -157,8 +157,8 @@ lib_deps =
emotibit/EmotiBit MLX90632@1.0.8 emotibit/EmotiBit MLX90632@1.0.8
# renovate: datasource=custom.pio depName=Adafruit MLX90614 packageName=adafruit/library/Adafruit MLX90614 Library # renovate: datasource=custom.pio depName=Adafruit MLX90614 packageName=adafruit/library/Adafruit MLX90614 Library
adafruit/Adafruit MLX90614 Library@2.1.5 adafruit/Adafruit MLX90614 Library@2.1.5
# renovate: datasource=github-tags depName=INA3221 packageName=KodinLanewave/INA3221 # renovate: datasource=github-tags depName=INA3221 packageName=sgtwilko/INA3221
https://github.com/KodinLanewave/INA3221/archive/1.0.1.zip https://github.com/sgtwilko/INA3221#bb03d7e9bfcc74fc798838a54f4f99738f29fc6a
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass # renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
mprograms/QMC5883LCompass@1.2.3 mprograms/QMC5883LCompass@1.2.3
# renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU # renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU
@@ -177,6 +177,8 @@ lib_deps =
adafruit/Adafruit PCT2075@1.0.5 adafruit/Adafruit PCT2075@1.0.5
# renovate: datasource=custom.pio depName=DFRobot_BMM150 packageName=dfrobot/library/DFRobot_BMM150 # renovate: datasource=custom.pio depName=DFRobot_BMM150 packageName=dfrobot/library/DFRobot_BMM150
dfrobot/DFRobot_BMM150@1.0.0 dfrobot/DFRobot_BMM150@1.0.0
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
adafruit/Adafruit TSL2561@1.1.2
; (not included in native / portduino) ; (not included in native / portduino)
[environmental_extra] [environmental_extra]
+1 -1
View File
@@ -146,7 +146,7 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess
{ {
int result; int result;
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
bool utf = !settingsMap[ascii_logs]; bool utf = !portduino_config.ascii_logs;
#else #else
bool utf = true; bool utf = true;
#endif #endif
+42
View File
@@ -38,4 +38,46 @@ const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaC
return useShortName ? "Custom" : "Invalid"; return useShortName ? "Custom" : "Invalid";
break; break;
} }
}
const char *DisplayFormatters::getDeviceRole(meshtastic_Config_DeviceConfig_Role role)
{
switch (role) {
case meshtastic_Config_DeviceConfig_Role_CLIENT:
return "Client";
break;
case meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE:
return "Client Mute";
break;
case meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN:
return "Client Hidden";
break;
case meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND:
return "Lost and Found";
break;
case meshtastic_Config_DeviceConfig_Role_TRACKER:
return "Tracker";
break;
case meshtastic_Config_DeviceConfig_Role_SENSOR:
return "Sensor";
break;
case meshtastic_Config_DeviceConfig_Role_TAK:
return "TAK";
break;
case meshtastic_Config_DeviceConfig_Role_TAK_TRACKER:
return "TAK Tracker";
break;
case meshtastic_Config_DeviceConfig_Role_ROUTER:
return "Router";
break;
case meshtastic_Config_DeviceConfig_Role_ROUTER_LATE:
return "Router Late";
break;
case meshtastic_Config_DeviceConfig_Role_REPEATER:
return "Repeater";
break;
default:
return "Unknown";
break;
}
} }
+3
View File
@@ -6,4 +6,7 @@ class DisplayFormatters
public: public:
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName, static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
bool usePreset); bool usePreset);
public:
static const char *getDeviceRole(meshtastic_Config_DeviceConfig_Role role);
}; };
+9
View File
@@ -22,6 +22,9 @@ class GPSStatus : public Status
meshtastic_Position p = meshtastic_Position_init_default; meshtastic_Position p = meshtastic_Position_init_default;
/// Time of last valid GPS fix (millis since boot)
uint32_t lastFixMillis = 0;
public: public:
GPSStatus() { statusType = STATUS_TYPE_GPS; } GPSStatus() { statusType = STATUS_TYPE_GPS; }
@@ -83,6 +86,9 @@ class GPSStatus : public Status
uint32_t getNumSatellites() const { return p.sats_in_view; } uint32_t getNumSatellites() const { return p.sats_in_view; }
/// Return millis() when the last GPS fix occurred (0 = never)
uint32_t getLastFixMillis() const { return lastFixMillis; }
bool matches(const GPSStatus *newStatus) const bool matches(const GPSStatus *newStatus) const
{ {
#ifdef GPS_DEBUG #ifdef GPS_DEBUG
@@ -114,6 +120,9 @@ class GPSStatus : public Status
if (isDirty) { if (isDirty) {
if (hasLock) { if (hasLock) {
// Record time of last valid GPS fix
lastFixMillis = millis();
// In debug logs, identify position by @timestamp:stage (stage 3 = notify) // In debug logs, identify position by @timestamp:stage (stage 3 = notify)
LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d", p.timestamp, LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d", p.timestamp,
p.latitude_i * 1e-7, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5, p.latitude_i * 1e-7, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5,
+24 -11
View File
@@ -833,16 +833,25 @@ void Power::readPowerStatus()
newStatus.notifyObservers(&powerStatus2); newStatus.notifyObservers(&powerStatus2);
#ifdef DEBUG_HEAP #ifdef DEBUG_HEAP
if (lastheap != memGet.getFreeHeap()) { if (lastheap != memGet.getFreeHeap()) {
std::string threadlist = "Threads running:"; // Use stack-allocated buffer to avoid heap allocations in monitoring code
char threadlist[256] = "Threads running:";
int threadlistLen = strlen(threadlist);
int running = 0; int running = 0;
for (int i = 0; i < MAX_THREADS; i++) { for (int i = 0; i < MAX_THREADS; i++) {
auto thread = concurrency::mainController.get(i); auto thread = concurrency::mainController.get(i);
if ((thread != nullptr) && (thread->enabled)) { if ((thread != nullptr) && (thread->enabled)) {
threadlist += vformat(" %s", thread->ThreadName.c_str()); // Use snprintf to safely append to stack buffer without heap allocation
int remaining = sizeof(threadlist) - threadlistLen - 1;
if (remaining > 0) {
int written = snprintf(threadlist + threadlistLen, remaining, " %s", thread->ThreadName.c_str());
if (written > 0 && written < remaining) {
threadlistLen += written;
}
}
running++; running++;
} }
} }
LOG_DEBUG(threadlist.c_str()); LOG_DEBUG(threadlist);
LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(), LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads", memGet.getFreeHeap(), memGet.getHeapSize(),
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false)); memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
lastheap = memGet.getFreeHeap(); lastheap = memGet.getFreeHeap();
@@ -856,15 +865,19 @@ void Power::readPowerStatus()
sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]); sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]);
auto newHeap = memGet.getFreeHeap(); auto newHeap = memGet.getFreeHeap();
std::string heapTopic = // Use stack-allocated buffers to avoid heap allocations in monitoring code
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/heap/") + std::string(mac); char heapTopic[128];
std::string heapString = std::to_string(newHeap); snprintf(heapTopic, sizeof(heapTopic), "%s/2/heap/%s", (*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh"), mac);
mqtt->pubSub.publish(heapTopic.c_str(), heapString.c_str(), false); char heapString[16];
snprintf(heapString, sizeof(heapString), "%u", newHeap);
mqtt->pubSub.publish(heapTopic, heapString, false);
auto wifiRSSI = WiFi.RSSI(); auto wifiRSSI = WiFi.RSSI();
std::string wifiTopic = char wifiTopic[128];
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/wifi/") + std::string(mac); snprintf(wifiTopic, sizeof(wifiTopic), "%s/2/wifi/%s", (*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh"), mac);
std::string wifiString = std::to_string(wifiRSSI); char wifiString[16];
mqtt->pubSub.publish(wifiTopic.c_str(), wifiString.c_str(), false); snprintf(wifiString, sizeof(wifiString), "%d", wifiRSSI);
mqtt->pubSub.publish(wifiTopic, wifiString, false);
} }
#endif #endif
+7 -7
View File
@@ -57,7 +57,7 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l
#endif #endif
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
bool color = !settingsMap[ascii_logs]; bool color = !portduino_config.ascii_logs;
#else #else
bool color = true; bool color = true;
#endif #endif
@@ -99,7 +99,7 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format,
size_t r = 0; size_t r = 0;
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
bool color = !settingsMap[ascii_logs]; bool color = !portduino_config.ascii_logs;
#else #else
bool color = true; bool color = true;
#endif #endif
@@ -288,7 +288,7 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
// level trace is special, two possible ways to handle it. // level trace is special, two possible ways to handle it.
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) { if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
if (settingsStrings[traceFilename] != "") { if (portduino_config.traceFilename != "") {
va_list arg; va_list arg;
va_start(arg, format); va_start(arg, format);
try { try {
@@ -297,18 +297,18 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
} }
va_end(arg); va_end(arg);
} }
if (settingsMap[logoutputlevel] < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) { if (portduino_config.logoutputlevel < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
delete[] newFormat; delete[] newFormat;
return; return;
} }
} }
if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) { if (portduino_config.logoutputlevel < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
delete[] newFormat; delete[] newFormat;
return; return;
} else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) { } else if (portduino_config.logoutputlevel < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) {
delete[] newFormat; delete[] newFormat;
return; return;
} else if (settingsMap[logoutputlevel] < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) { } else if (portduino_config.logoutputlevel < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) {
delete[] newFormat; delete[] newFormat;
return; return;
} }
+1
View File
@@ -431,6 +431,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MESHTASTIC_EXCLUDE_SERIAL 1 #define MESHTASTIC_EXCLUDE_SERIAL 1
#define MESHTASTIC_EXCLUDE_POWERSTRESS 1 #define MESHTASTIC_EXCLUDE_POWERSTRESS 1
#define MESHTASTIC_EXCLUDE_ADMIN 1 #define MESHTASTIC_EXCLUDE_ADMIN 1
#define MESHTASTIC_EXCLUDE_AMBIENTLIGHTING 1
#endif #endif
// // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled) // // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled)
+1
View File
@@ -80,6 +80,7 @@ class ScanI2C
LTR553ALS, LTR553ALS,
BHI260AP, BHI260AP,
BMM150, BMM150,
TSL2561,
DRV2605 DRV2605
} DeviceType; } DeviceType;
+11 -1
View File
@@ -461,7 +461,17 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address); SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address); SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address); SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TSL25911_ADDR, TSL2591, "TSL2591", (uint8_t)addr.address); case TSL25911_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x12), 1);
if (registerValue == 0x50) {
type = TSL2591;
logFoundDevice("TSL25911", (uint8_t)addr.address);
} else {
type = TSL2561;
logFoundDevice("TSL2561", (uint8_t)addr.address);
}
break;
SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632", (uint8_t)addr.address); SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802", (uint8_t)addr.address); SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048", (uint8_t)addr.address); SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048", (uint8_t)addr.address);
+19 -6
View File
@@ -808,6 +808,14 @@ bool GPS::setup()
} else { } else {
LOG_INFO("GNSS module configuration saved!"); LOG_INFO("GNSS module configuration saved!");
} }
} else if (gnssModel == GNSS_MODEL_CM121) {
// only ask for RMC and GGA
// enable GGA
_serial_gps->write("$CFGMSG,0,0,1,1*1B\r\n");
delay(250);
// enable RMC
_serial_gps->write("$CFGMSG,0,4,1,1*1F\r\n");
delay(250);
} }
didSerialInit = true; didSerialInit = true;
} }
@@ -1240,9 +1248,15 @@ GnssModel_t GPS::probe(int serialSpeed)
_serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n"); _serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n");
_serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n"); _serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n");
delay(20); delay(20);
// Close NMEA sequences on CM121
_serial_gps->write("$CFGMSG,0,1,0,1*1B\r\n");
_serial_gps->write("$CFGMSG,0,2,0,1*18\r\n");
_serial_gps->write("$CFGMSG,0,3,0,1*19\r\n");
delay(20);
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A // Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121
std::vector<ChipInfo> unicore = {{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}}; std::vector<ChipInfo> unicore = {
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}};
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500); PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
std::vector<ChipInfo> atgm = { std::vector<ChipInfo> atgm = {
@@ -1422,7 +1436,7 @@ GPS *GPS::createGps()
_en_gpio = PIN_GPS_EN; _en_gpio = PIN_GPS_EN;
#endif #endif
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
if (!settingsMap[has_gps]) if (!portduino_config.has_gps)
return nullptr; return nullptr;
#endif #endif
if (!_rx_gpio || !_serial_gps) // Configured to have no GPS at all if (!_rx_gpio || !_serial_gps) // Configured to have no GPS at all
@@ -1532,10 +1546,9 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
t.tm_year = d.year() - 1900; t.tm_year = d.year() - 1900;
t.tm_isdst = false; t.tm_isdst = false;
if (t.tm_mon > -1) { if (t.tm_mon > -1) {
LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
t.tm_sec, ti.age());
if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultSuccess) { if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultSuccess) {
LOG_DEBUG("Time set."); LOG_DEBUG("NMEA GPS time set %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour,
t.tm_min, t.tm_sec, ti.age());
return true; return true;
} else { } else {
return false; return false;
+2 -1
View File
@@ -31,7 +31,8 @@ typedef enum {
GNSS_MODEL_MTK_PA1616S, GNSS_MODEL_MTK_PA1616S,
GNSS_MODEL_AG3335, GNSS_MODEL_AG3335,
GNSS_MODEL_AG3352, GNSS_MODEL_AG3352,
GNSS_MODEL_LS20031 GNSS_MODEL_LS20031,
GNSS_MODEL_CM121
} GnssModel_t; } GnssModel_t;
typedef enum { typedef enum {
+8
View File
@@ -130,11 +130,15 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms uint32_t printableEpoch = tv->tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
#ifdef BUILD_EPOCH #ifdef BUILD_EPOCH
if (tv->tv_sec < BUILD_EPOCH) { if (tv->tv_sec < BUILD_EPOCH) {
#ifdef GPS_DEBUG
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
#endif
return RTCSetResultInvalidTime; return RTCSetResultInvalidTime;
} else if (tv->tv_sec > (BUILD_EPOCH + FORTY_YEARS)) { } else if (tv->tv_sec > (BUILD_EPOCH + FORTY_YEARS)) {
#ifdef GPS_DEBUG
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH, LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH,
BUILD_EPOCH + FORTY_YEARS); BUILD_EPOCH + FORTY_YEARS);
#endif
return RTCSetResultInvalidTime; return RTCSetResultInvalidTime;
} }
#endif #endif
@@ -252,11 +256,15 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
#ifdef BUILD_EPOCH #ifdef BUILD_EPOCH
if (tv.tv_sec < BUILD_EPOCH) { if (tv.tv_sec < BUILD_EPOCH) {
#ifdef GPS_DEBUG
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH); LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
#endif
return RTCSetResultInvalidTime; return RTCSetResultInvalidTime;
} else if (tv.tv_sec > (BUILD_EPOCH + FORTY_YEARS)) { } else if (tv.tv_sec > (BUILD_EPOCH + FORTY_YEARS)) {
#ifdef GPS_DEBUG
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH, LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH,
BUILD_EPOCH + FORTY_YEARS); BUILD_EPOCH + FORTY_YEARS);
#endif
return RTCSetResultInvalidTime; return RTCSetResultInvalidTime;
} }
#endif #endif
+12 -2
View File
@@ -370,7 +370,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif ARCH_PORTDUINO #elif ARCH_PORTDUINO
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
if (settingsMap[displayPanel] != no_screen) { if (portduino_config.displayPanel != no_screen) {
LOG_DEBUG("Make TFTDisplay!"); LOG_DEBUG("Make TFTDisplay!");
dispdev = new TFTDisplay(address.address, -1, -1, geometry, dispdev = new TFTDisplay(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
@@ -618,7 +618,7 @@ void Screen::setup()
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
if (settingsMap[touchscreenModule]) { if (portduino_config.touchscreenModule) {
touchScreenImpl1 = touchScreenImpl1 =
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch); new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
touchScreenImpl1->init(); touchScreenImpl1->init();
@@ -1018,6 +1018,11 @@ void Screen::setFrames(FrameFocus focus)
indicatorIcons.push_back(digital_icon_clock); indicatorIcons.push_back(digital_icon_clock);
} }
#endif #endif
if (!hiddenFrames.chirpy) {
fsi.positions.chirpy = numframes;
normalFrames[numframes++] = graphics::DebugRenderer::drawChirpy;
indicatorIcons.push_back(small_chirpy);
}
#if HAS_WIFI && !defined(ARCH_PORTDUINO) #if HAS_WIFI && !defined(ARCH_PORTDUINO)
if (!hiddenFrames.wifi && isWifiAvailable()) { if (!hiddenFrames.wifi && isWifiAvailable()) {
@@ -1186,6 +1191,9 @@ void Screen::toggleFrameVisibility(const std::string &frameName)
if (frameName == "show_favorites") { if (frameName == "show_favorites") {
hiddenFrames.show_favorites = !hiddenFrames.show_favorites; hiddenFrames.show_favorites = !hiddenFrames.show_favorites;
} }
if (frameName == "chirpy") {
hiddenFrames.chirpy = !hiddenFrames.chirpy;
}
} }
bool Screen::isFrameHidden(const std::string &frameName) const bool Screen::isFrameHidden(const std::string &frameName) const
@@ -1214,6 +1222,8 @@ bool Screen::isFrameHidden(const std::string &frameName) const
return hiddenFrames.clock; return hiddenFrames.clock;
if (frameName == "show_favorites") if (frameName == "show_favorites")
return hiddenFrames.show_favorites; return hiddenFrames.show_favorites;
if (frameName == "chirpy")
return hiddenFrames.chirpy;
return false; return false;
} }
+2
View File
@@ -669,6 +669,7 @@ class Screen : public concurrency::OSThread
uint8_t nodelist_distance = 255; uint8_t nodelist_distance = 255;
uint8_t nodelist_bearings = 255; uint8_t nodelist_bearings = 255;
uint8_t clock = 255; uint8_t clock = 255;
uint8_t chirpy = 255;
uint8_t firstFavorite = 255; uint8_t firstFavorite = 255;
uint8_t lastFavorite = 255; uint8_t lastFavorite = 255;
uint8_t lora = 255; uint8_t lora = 255;
@@ -698,6 +699,7 @@ class Screen : public concurrency::OSThread
#endif #endif
bool lora = false; bool lora = false;
bool show_favorites = false; bool show_favorites = false;
bool chirpy = true;
} hiddenFrames; } hiddenFrames;
/// Try to start drawing ASAP /// Try to start drawing ASAP
+41 -10
View File
@@ -1,6 +1,7 @@
#include "graphics/SharedUIDisplay.h" #include "graphics/SharedUIDisplay.h"
#include "RTC.h" #include "RTC.h"
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#include "graphics/draw/UIRenderer.h"
#include "main.h" #include "main.h"
#include "meshtastic/config.pb.h" #include "meshtastic/config.pb.h"
#include "power.h" #include "power.h"
@@ -16,6 +17,10 @@ void determineResolution(int16_t screenheight, int16_t screenwidth)
isHighResolution = true; isHighResolution = true;
} }
if (screenwidth > 128 && screenheight <= 64) {
isHighResolution = false;
}
// Special case for Heltec Wireless Tracker v1.1 // Special case for Heltec Wireless Tracker v1.1
if (screenwidth == 160 && screenheight == 80) { if (screenwidth == 160 && screenheight == 80) {
isHighResolution = false; isHighResolution = false;
@@ -53,7 +58,7 @@ void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w,
// ************************* // *************************
// * Common Header Drawing * // * Common Header Drawing *
// ************************* // *************************
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool battery_only) void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool force_no_invert, bool show_date)
{ {
constexpr int HEADER_OFFSET_Y = 1; constexpr int HEADER_OFFSET_Y = 1;
y += HEADER_OFFSET_Y; y += HEADER_OFFSET_Y;
@@ -69,7 +74,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
const int screenW = display->getWidth(); const int screenW = display->getWidth();
const int screenH = display->getHeight(); const int screenH = display->getHeight();
if (!battery_only) { if (!force_no_invert) {
// === Inverted Header Background === // === Inverted Header Background ===
if (isInverted) { if (isInverted) {
display->setColor(BLACK); display->setColor(BLACK);
@@ -187,13 +192,28 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
int timeStrWidth = display->getStringWidth("12:34"); // Default alignment int timeStrWidth = display->getStringWidth("12:34"); // Default alignment
int timeX = screenW - xOffset - timeStrWidth + 4; int timeX = screenW - xOffset - timeStrWidth + 4;
if (rtc_sec > 0 && !battery_only) { if (rtc_sec > 0) {
// === Build Time String === // === Build Time String ===
long hms = (rtc_sec % SEC_PER_DAY + SEC_PER_DAY) % SEC_PER_DAY; long hms = (rtc_sec % SEC_PER_DAY + SEC_PER_DAY) % SEC_PER_DAY;
int hour = hms / SEC_PER_HOUR; int hour = hms / SEC_PER_HOUR;
int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN; int minute = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
snprintf(timeStr, sizeof(timeStr), "%d:%02d", hour, minute); snprintf(timeStr, sizeof(timeStr), "%d:%02d", hour, minute);
// === Build Date String ===
char datetimeStr[25];
UIRenderer::formatDateTime(datetimeStr, sizeof(datetimeStr), rtc_sec, display, false);
char dateLine[40];
if (isHighResolution) {
snprintf(dateLine, sizeof(dateLine), "%s", datetimeStr);
} else {
if (hasUnreadMessage) {
snprintf(dateLine, sizeof(dateLine), "%s", &datetimeStr[5]);
} else {
snprintf(dateLine, sizeof(dateLine), "%s", &datetimeStr[2]);
}
}
if (config.display.use_12h_clock) { if (config.display.use_12h_clock) {
bool isPM = hour >= 12; bool isPM = hour >= 12;
hour %= 12; hour %= 12;
@@ -202,7 +222,11 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
snprintf(timeStr, sizeof(timeStr), "%d:%02d%s", hour, minute, isPM ? "p" : "a"); snprintf(timeStr, sizeof(timeStr), "%d:%02d%s", hour, minute, isPM ? "p" : "a");
} }
timeStrWidth = display->getStringWidth(timeStr); if (show_date) {
timeStrWidth = display->getStringWidth(dateLine);
} else {
timeStrWidth = display->getStringWidth(timeStr);
}
timeX = screenW - xOffset - timeStrWidth + 3; timeX = screenW - xOffset - timeStrWidth + 3;
// === Show Mail or Mute Icon to the Left of Time === // === Show Mail or Mute Icon to the Left of Time ===
@@ -229,7 +253,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
int iconW = 16, iconH = 12; int iconW = 16, iconH = 12;
int iconX = iconRightEdge - iconW; int iconX = iconRightEdge - iconW;
int iconY = textY + (FONT_HEIGHT_SMALL - iconH) / 2 - 1; int iconY = textY + (FONT_HEIGHT_SMALL - iconH) / 2 - 1;
if (isInverted) { if (isInverted && !force_no_invert) {
display->setColor(WHITE); display->setColor(WHITE);
display->fillRect(iconX - 1, iconY - 1, iconW + 3, iconH + 2); display->fillRect(iconX - 1, iconY - 1, iconW + 3, iconH + 2);
display->setColor(BLACK); display->setColor(BLACK);
@@ -244,7 +268,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
} else { } else {
int iconX = iconRightEdge - (mail_width - 2); int iconX = iconRightEdge - (mail_width - 2);
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2; int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
if (isInverted) { if (isInverted && !force_no_invert) {
display->setColor(WHITE); display->setColor(WHITE);
display->fillRect(iconX - 1, iconY - 1, mail_width + 2, mail_height + 2); display->fillRect(iconX - 1, iconY - 1, mail_width + 2, mail_height + 2);
display->setColor(BLACK); display->setColor(BLACK);
@@ -287,10 +311,17 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
} }
} }
// === Draw Time === if (show_date) {
display->drawString(timeX, textY, timeStr); // === Draw Date ===
if (isBold) display->drawString(timeX, textY, dateLine);
display->drawString(timeX - 1, textY, timeStr); if (isBold)
display->drawString(timeX - 1, textY, dateLine);
} else {
// === Draw Time ===
display->drawString(timeX, textY, timeStr);
if (isBold)
display->drawString(timeX - 1, textY, timeStr);
}
} else { } else {
// === No Time Available: Mail/Mute Icon Moves to Far Right === // === No Time Available: Mail/Mute Icon Moves to Far Right ===
+2 -1
View File
@@ -49,7 +49,8 @@ void determineResolution(int16_t screenheight, int16_t screenwidth);
void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r); void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w, int16_t h, int16_t r);
// Shared battery/time/mail header // Shared battery/time/mail header
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr = "", bool battery_only = false); void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr = "", bool force_no_invert = false,
bool show_date = false);
const int *getTextPositions(OLEDDisplay *display); const int *getTextPositions(OLEDDisplay *display);
+53 -44
View File
@@ -767,24 +767,24 @@ class LGFX : public lgfx::LGFX_Device
LGFX(void) LGFX(void)
{ {
if (settingsMap[displayPanel] == st7789) if (portduino_config.displayPanel == st7789)
_panel_instance = new lgfx::Panel_ST7789; _panel_instance = new lgfx::Panel_ST7789;
else if (settingsMap[displayPanel] == st7735) else if (portduino_config.displayPanel == st7735)
_panel_instance = new lgfx::Panel_ST7735; _panel_instance = new lgfx::Panel_ST7735;
else if (settingsMap[displayPanel] == st7735s) else if (portduino_config.displayPanel == st7735s)
_panel_instance = new lgfx::Panel_ST7735S; _panel_instance = new lgfx::Panel_ST7735S;
else if (settingsMap[displayPanel] == st7796) else if (portduino_config.displayPanel == st7796)
_panel_instance = new lgfx::Panel_ST7796; _panel_instance = new lgfx::Panel_ST7796;
else if (settingsMap[displayPanel] == ili9341) else if (portduino_config.displayPanel == ili9341)
_panel_instance = new lgfx::Panel_ILI9341; _panel_instance = new lgfx::Panel_ILI9341;
else if (settingsMap[displayPanel] == ili9342) else if (portduino_config.displayPanel == ili9342)
_panel_instance = new lgfx::Panel_ILI9342; _panel_instance = new lgfx::Panel_ILI9342;
else if (settingsMap[displayPanel] == ili9488) else if (portduino_config.displayPanel == ili9488)
_panel_instance = new lgfx::Panel_ILI9488; _panel_instance = new lgfx::Panel_ILI9488;
else if (settingsMap[displayPanel] == hx8357d) else if (portduino_config.displayPanel == hx8357d)
_panel_instance = new lgfx::Panel_HX8357D; _panel_instance = new lgfx::Panel_HX8357D;
#if defined(LGFX_SDL) #if defined(LGFX_SDL)
else if (settingsMap[displayPanel] == x11) { else if (portduino_config.displayPanel == x11) {
_panel_instance = new lgfx::Panel_sdl; _panel_instance = new lgfx::Panel_sdl;
} }
#endif #endif
@@ -795,61 +795,61 @@ class LGFX : public lgfx::LGFX_Device
auto buscfg = _bus_instance.config(); auto buscfg = _bus_instance.config();
buscfg.spi_mode = 0; buscfg.spi_mode = 0;
buscfg.spi_host = settingsMap[displayspidev]; buscfg.spi_host = portduino_config.display_spi_dev_int;
buscfg.pin_dc = settingsMap[displayDC]; // Set SPI DC pin number (-1 = disable) buscfg.pin_dc = portduino_config.displayDC.pin; // Set SPI DC pin number (-1 = disable)
_bus_instance.config(buscfg); // applies the set value to the bus. _bus_instance.config(buscfg); // applies the set value to the bus.
_panel_instance->setBus(&_bus_instance); // set the bus on the panel. _panel_instance->setBus(&_bus_instance); // set the bus on the panel.
auto cfg = _panel_instance->config(); // Gets a structure for display panel settings. auto cfg = _panel_instance->config(); // Gets a structure for display panel settings.
LOG_DEBUG("Width: %d, Height: %d", settingsMap[displayWidth], settingsMap[displayHeight]); LOG_DEBUG("Width: %d, Height: %d", portduino_config.displayWidth, portduino_config.displayHeight);
cfg.pin_cs = settingsMap[displayCS]; // Pin number where CS is connected (-1 = disable) cfg.pin_cs = portduino_config.displayCS.pin; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = settingsMap[displayReset]; cfg.pin_rst = portduino_config.displayReset.pin;
if (settingsMap[displayRotate]) { if (portduino_config.displayRotate) {
cfg.panel_width = settingsMap[displayHeight]; // actual displayable width cfg.panel_width = portduino_config.displayHeight; // actual displayable width
cfg.panel_height = settingsMap[displayWidth]; // actual displayable height cfg.panel_height = portduino_config.displayWidth; // actual displayable height
} else { } else {
cfg.panel_width = settingsMap[displayWidth]; // actual displayable width cfg.panel_width = portduino_config.displayWidth; // actual displayable width
cfg.panel_height = settingsMap[displayHeight]; // actual displayable height cfg.panel_height = portduino_config.displayHeight; // actual displayable height
} }
cfg.offset_x = settingsMap[displayOffsetX]; // Panel offset amount in X direction cfg.offset_x = portduino_config.displayOffsetX; // Panel offset amount in X direction
cfg.offset_y = settingsMap[displayOffsetY]; // Panel offset amount in Y direction cfg.offset_y = portduino_config.displayOffsetY; // Panel offset amount in Y direction
cfg.offset_rotation = settingsMap[displayOffsetRotate]; // Rotation direction value offset 0~7 (4~7 is mirrored) cfg.offset_rotation = portduino_config.displayOffsetRotate; // Rotation direction value offset 0~7 (4~7 is mirrored)
cfg.invert = settingsMap[displayInvert]; // Set to true if the light/darkness of the panel is reversed cfg.invert = portduino_config.displayInvert; // Set to true if the light/darkness of the panel is reversed
_panel_instance->config(cfg); _panel_instance->config(cfg);
// Configure settings for touch control. // Configure settings for touch control.
if (settingsMap[touchscreenModule]) { if (portduino_config.touchscreenModule) {
if (settingsMap[touchscreenModule] == xpt2046) { if (portduino_config.touchscreenModule == xpt2046) {
_touch_instance = new lgfx::Touch_XPT2046; _touch_instance = new lgfx::Touch_XPT2046;
} else if (settingsMap[touchscreenModule] == stmpe610) { } else if (portduino_config.touchscreenModule == stmpe610) {
_touch_instance = new lgfx::Touch_STMPE610; _touch_instance = new lgfx::Touch_STMPE610;
} else if (settingsMap[touchscreenModule] == ft5x06) { } else if (portduino_config.touchscreenModule == ft5x06) {
_touch_instance = new lgfx::Touch_FT5x06; _touch_instance = new lgfx::Touch_FT5x06;
} }
auto touch_cfg = _touch_instance->config(); auto touch_cfg = _touch_instance->config();
touch_cfg.pin_cs = settingsMap[touchscreenCS]; touch_cfg.pin_cs = portduino_config.touchscreenCS.pin;
touch_cfg.x_min = 0; touch_cfg.x_min = 0;
touch_cfg.x_max = settingsMap[displayHeight] - 1; touch_cfg.x_max = portduino_config.displayHeight - 1;
touch_cfg.y_min = 0; touch_cfg.y_min = 0;
touch_cfg.y_max = settingsMap[displayWidth] - 1; touch_cfg.y_max = portduino_config.displayWidth - 1;
touch_cfg.pin_int = settingsMap[touchscreenIRQ]; touch_cfg.pin_int = portduino_config.touchscreenIRQ.pin;
touch_cfg.bus_shared = true; touch_cfg.bus_shared = true;
touch_cfg.offset_rotation = settingsMap[touchscreenRotate]; touch_cfg.offset_rotation = portduino_config.touchscreenRotate;
if (settingsMap[touchscreenI2CAddr] != -1) { if (portduino_config.touchscreenI2CAddr != -1) {
touch_cfg.i2c_addr = settingsMap[touchscreenI2CAddr]; touch_cfg.i2c_addr = portduino_config.touchscreenI2CAddr;
} else { } else {
touch_cfg.spi_host = settingsMap[touchscreenspidev]; touch_cfg.spi_host = portduino_config.touchscreen_spi_dev_int;
} }
_touch_instance->config(touch_cfg); _touch_instance->config(touch_cfg);
_panel_instance->setTouch(_touch_instance); _panel_instance->setTouch(_touch_instance);
} }
#if defined(LGFX_SDL) #if defined(LGFX_SDL)
if (settingsMap[displayPanel] == x11) { if (portduino_config.displayPanel == x11) {
lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)_panel_instance; lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)_panel_instance;
sdl_panel_->setup(); sdl_panel_->setup();
sdl_panel_->addKeyCodeMapping(SDLK_RETURN, SDL_SCANCODE_KP_ENTER); sdl_panel_->addKeyCodeMapping(SDLK_RETURN, SDL_SCANCODE_KP_ENTER);
@@ -1115,10 +1115,10 @@ TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY g
backlightEnable = p; backlightEnable = p;
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (settingsMap[displayRotate]) { if (portduino_config.displayRotate) {
setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayWidth], settingsMap[configNames::displayHeight]); setGeometry(GEOMETRY_RAWMODE, portduino_config.displayWidth, portduino_config.displayWidth);
} else { } else {
setGeometry(GEOMETRY_RAWMODE, settingsMap[configNames::displayHeight], settingsMap[configNames::displayWidth]); setGeometry(GEOMETRY_RAWMODE, portduino_config.displayHeight, portduino_config.displayHeight);
} }
#elif defined(SCREEN_ROTATE) #elif defined(SCREEN_ROTATE)
@@ -1128,6 +1128,15 @@ TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY g
#endif #endif
} }
TFTDisplay::~TFTDisplay()
{
// Clean up allocated line pixel buffer to prevent memory leak
if (linePixelBuffer != nullptr) {
free(linePixelBuffer);
linePixelBuffer = nullptr;
}
}
// Write the buffer to the display memory // Write the buffer to the display memory
void TFTDisplay::display(bool fromBlank) void TFTDisplay::display(bool fromBlank)
{ {
@@ -1231,7 +1240,7 @@ void TFTDisplay::sdlLoop()
#if defined(LGFX_SDL) #if defined(LGFX_SDL)
static int lastPressed = 0; static int lastPressed = 0;
static int shuttingDown = false; static int shuttingDown = false;
if (settingsMap[displayPanel] == x11) { if (portduino_config.displayPanel == x11) {
lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)tft->_panel_instance; lgfx::Panel_sdl *sdl_panel_ = (lgfx::Panel_sdl *)tft->_panel_instance;
if (sdl_panel_->loop() && !shuttingDown) { if (sdl_panel_->loop() && !shuttingDown) {
LOG_WARN("Window Closed!"); LOG_WARN("Window Closed!");
@@ -1279,8 +1288,8 @@ void TFTDisplay::sendCommand(uint8_t com)
backlightEnable->set(true); backlightEnable->set(true);
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
display(true); display(true);
if (settingsMap[displayBacklight] > 0) if (portduino_config.displayBacklight.pin > 0)
digitalWrite(settingsMap[displayBacklight], TFT_BACKLIGHT_ON); digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON);
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
tft->wakeup(); tft->wakeup();
tft->powerSaveOff(); tft->powerSaveOff();
@@ -1303,8 +1312,8 @@ void TFTDisplay::sendCommand(uint8_t com)
backlightEnable->set(false); backlightEnable->set(false);
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
tft->clear(); tft->clear();
if (settingsMap[displayBacklight] > 0) if (portduino_config.displayBacklight.pin > 0)
digitalWrite(settingsMap[displayBacklight], !TFT_BACKLIGHT_ON); digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON);
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
tft->sleep(); tft->sleep();
tft->powerSaveOn(); tft->powerSaveOn();
+3
View File
@@ -20,6 +20,9 @@ class TFTDisplay : public OLEDDisplay
*/ */
TFTDisplay(uint8_t, int, int, OLEDDISPLAY_GEOMETRY, HW_I2C); TFTDisplay(uint8_t, int, int, OLEDDISPLAY_GEOMETRY, HW_I2C);
// Destructor to clean up allocated memory
~TFTDisplay();
// Write the buffer to the display memory // Write the buffer to the display memory
virtual void display() override { display(false); }; virtual void display() override { display(false); };
virtual void display(bool fromBlank); virtual void display(bool fromBlank);
+3 -3
View File
@@ -2,7 +2,6 @@
#if HAS_SCREEN #if HAS_SCREEN
#include "ClockRenderer.h" #include "ClockRenderer.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "UIRenderer.h"
#include "configuration.h" #include "configuration.h"
#include "gps/GeoCoord.h" #include "gps/GeoCoord.h"
#include "gps/RTC.h" #include "gps/RTC.h"
@@ -190,7 +189,7 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
// === Set Title, Blank for Clock // === Set Title, Blank for Clock
const char *titleStr = ""; const char *titleStr = "";
// === Header === // === Header ===
graphics::drawCommonHeader(display, x, y, titleStr, true); graphics::drawCommonHeader(display, x, y, titleStr, true, true);
#ifdef T_WATCH_S3 #ifdef T_WATCH_S3
if (nimbleBluetooth && nimbleBluetooth->isConnected()) { if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
@@ -294,6 +293,7 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2, display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
isPM ? "pm" : "am"); isPM ? "pm" : "am");
} }
#ifndef USE_EINK #ifndef USE_EINK
xOffset = (isHighResolution) ? 18 : 10; xOffset = (isHighResolution) ? 18 : 10;
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - yOffset, display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - yOffset,
@@ -313,7 +313,7 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// === Set Title, Blank for Clock // === Set Title, Blank for Clock
const char *titleStr = ""; const char *titleStr = "";
// === Header === // === Header ===
graphics::drawCommonHeader(display, x, y, titleStr, true); graphics::drawCommonHeader(display, x, y, titleStr, true, true);
#ifdef T_WATCH_S3 #ifdef T_WATCH_S3
if (nimbleBluetooth && nimbleBluetooth->isConnected()) { if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
+42 -6
View File
@@ -391,18 +391,27 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
int nameX = (SCREEN_WIDTH - textWidth); int nameX = (SCREEN_WIDTH - textWidth);
display->drawString(nameX, getTextPositions(display)[line++], shortnameble); display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
// === Second Row: Radio Preset === // === Second Row: Role ===
auto role = DisplayFormatters::getDeviceRole(config.device.role);
char device_role[25];
snprintf(device_role, sizeof(device_role), "Role: %s", role);
textWidth = display->getStringWidth(device_role);
nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line++], device_role);
// === Third Row: Radio Preset ===
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset); auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
char regionradiopreset[25]; char regionradiopreset[25];
const char *region = myRegion ? myRegion->name : NULL; const char *region = myRegion ? myRegion->name : NULL;
if (region != nullptr) { if (region != nullptr) {
snprintf(regionradiopreset, sizeof(regionradiopreset), "%s/%s", region, mode); snprintf(regionradiopreset, sizeof(regionradiopreset), "Reg: %s/%s", region, mode);
} }
textWidth = display->getStringWidth(regionradiopreset); textWidth = display->getStringWidth(regionradiopreset);
nameX = (SCREEN_WIDTH - textWidth) / 2; nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line++], regionradiopreset); display->drawString(nameX, getTextPositions(display)[line++], regionradiopreset);
// === Third Row: Frequency / ChanNum === // === Fourth Row: Frequency / ChanNum ===
char frequencyslot[35]; char frequencyslot[35];
char freqStr[16]; char freqStr[16];
float freq = RadioLibInterface::instance->getFreq(); float freq = RadioLibInterface::instance->getFreq();
@@ -420,7 +429,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
nameX = (SCREEN_WIDTH - textWidth) / 2; nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line++], frequencyslot); display->drawString(nameX, getTextPositions(display)[line++], frequencyslot);
// === Fourth Row: Channel Utilization === // === Fifth Row: Channel Utilization ===
const char *chUtil = "ChUtil:"; const char *chUtil = "ChUtil:";
char chUtilPercentage[10]; char chUtilPercentage[10];
snprintf(chUtilPercentage, sizeof(chUtilPercentage), "%2.0f%%", airTime->channelUtilizationPercent()); snprintf(chUtilPercentage, sizeof(chUtilPercentage), "%2.0f%%", airTime->channelUtilizationPercent());
@@ -437,7 +446,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
int total_line_content_width = (chUtil_x + chutil_bar_width + display->getStringWidth(chUtilPercentage) + extraoffset) / 2; int total_line_content_width = (chUtil_x + chutil_bar_width + display->getStringWidth(chUtilPercentage) + extraoffset) / 2;
int starting_position = centerofscreen - total_line_content_width; int starting_position = centerofscreen - total_line_content_width;
display->drawString(starting_position, getTextPositions(display)[line++], chUtil); display->drawString(starting_position, getTextPositions(display)[line], chUtil);
// Force 56% or higher to show a full 100% bar, text would still show related percent. // Force 56% or higher to show a full 100% bar, text would still show related percent.
if (chutil_percent >= 61) { if (chutil_percent >= 61) {
@@ -474,7 +483,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
display->fillRect(starting_position + chUtil_x, chUtil_y, fillRight, chutil_bar_height); display->fillRect(starting_position + chUtil_x, chUtil_y, fillRight, chutil_bar_height);
} }
display->drawString(starting_position + chUtil_x + chutil_bar_width + extraoffset, getTextPositions(display)[4], display->drawString(starting_position + chUtil_x + chutil_bar_width + extraoffset, getTextPositions(display)[line++],
chUtilPercentage); chUtilPercentage);
} }
@@ -625,6 +634,33 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
display->drawString(nameX, getTextPositions(display)[line], uptimeStr); display->drawString(nameX, getTextPositions(display)[line], uptimeStr);
} }
} }
// ****************************
// * Chirpy Screen *
// ****************************
void drawChirpy(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->clear();
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
int line = 1;
int iconX = SCREEN_WIDTH - chirpy_width - (chirpy_width / 3);
int iconY = (SCREEN_HEIGHT - chirpy_height) / 2;
int textX_offset = 10;
if (isHighResolution) {
iconX = SCREEN_WIDTH - chirpy_width_hirez - (chirpy_width_hirez / 3);
iconY = (SCREEN_HEIGHT - chirpy_height_hirez) / 2;
textX_offset = textX_offset * 4;
display->drawXbm(iconX, iconY, chirpy_width_hirez, chirpy_height_hirez, chirpy_hirez);
} else {
display->drawXbm(iconX, iconY, chirpy_width, chirpy_height, chirpy);
}
int textX = (display->getWidth() / 2) - textX_offset - (display->getStringWidth("Hello") / 2);
display->drawString(textX, getTextPositions(display)[line++], "Hello");
textX = (display->getWidth() / 2) - textX_offset - (display->getStringWidth("World!") / 2);
display->drawString(textX, getTextPositions(display)[line++], "World!");
}
} // namespace DebugRenderer } // namespace DebugRenderer
} // namespace graphics } // namespace graphics
#endif #endif
+3
View File
@@ -33,6 +33,9 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
// System screen display // System screen display
void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
// Chirpy screen display
void drawChirpy(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
} // namespace DebugRenderer } // namespace DebugRenderer
} // namespace graphics } // namespace graphics
+68 -9
View File
@@ -31,17 +31,19 @@ uint8_t test_count = 0;
void menuHandler::loraMenu() void menuHandler::loraMenu()
{ {
static const char *optionsArray[] = {"Back", "Region Picker"}; static const char *optionsArray[] = {"Back", "Region Picker", "Device Role"};
enum optionsNumbers { Back = 0, lora_picker = 1 }; enum optionsNumbers { Back = 0, lora_picker = 1, device_role_picker = 2 };
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "LoRa Actions"; bannerOptions.message = "LoRa Actions";
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 2; bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) { if (selected == Back) {
// No action // No action
} else if (selected == lora_picker) { } else if (selected == lora_picker) {
menuHandler::menuQueue = menuHandler::lora_picker; menuHandler::menuQueue = menuHandler::lora_picker;
} else if (selected == device_role_picker) {
menuHandler::menuQueue = menuHandler::device_role_picker;
} }
}; };
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
@@ -140,6 +142,40 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::DeviceRolePicker()
{
static const char *optionsArray[] = {"Back", "Client", "Client Mute", "Lost and Found", "Tracker"};
enum optionsNumbers {
Back = 0,
devicerole_client = 1,
devicerole_clientmute = 2,
devicerole_lostandfound = 3,
devicerole_tracker = 4
};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Device Role";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 5;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Back) {
menuHandler::menuQueue = menuHandler::lora_Menu;
screen->runNow();
return;
} else if (selected == devicerole_client) {
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
} else if (selected == devicerole_clientmute) {
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE;
} else if (selected == devicerole_lostandfound) {
config.device.role = meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND;
} else if (selected == devicerole_tracker) {
config.device.role = meshtastic_Config_DeviceConfig_Role_TRACKER;
}
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::TwelveHourPicker() void menuHandler::TwelveHourPicker()
{ {
static const char *optionsArray[] = {"Back", "12-hour", "24-hour"}; static const char *optionsArray[] = {"Back", "12-hour", "24-hour"};
@@ -968,16 +1004,33 @@ void menuHandler::traceRouteMenu()
void menuHandler::testMenu() void menuHandler::testMenu()
{ {
static const char *optionsArray[] = {"Back", "Number Picker"}; enum optionsNumbers { Back, NumberPicker, ShowChirpy };
static const char *optionsArray[4] = {"Back"};
static int optionsEnumArray[4] = {Back};
int options = 1;
optionsArray[options] = "Number Picker";
optionsEnumArray[options++] = NumberPicker;
optionsArray[options] = screen->isFrameHidden("chirpy") ? "Show Chirpy" : "Hide Chirpy";
optionsEnumArray[options++] = ShowChirpy;
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
std::string message = "Test to Run?\n"; bannerOptions.message = "Hidden Test Menu";
bannerOptions.message = message.c_str();
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 2; bannerOptions.optionsCount = options;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) { if (selected == NumberPicker) {
menuQueue = number_test; menuQueue = number_test;
screen->runNow(); screen->runNow();
} else if (selected == ShowChirpy) {
screen->toggleFrameVisibility("chirpy");
screen->setFrames(Screen::FOCUS_SYSTEM);
} else {
menuQueue = system_base_menu;
screen->runNow();
} }
}; };
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
@@ -1232,7 +1285,7 @@ void menuHandler::FrameToggles_menu()
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.InitialSelected = lastSelectedIndex; // Use index, not enum value bannerOptions.InitialSelected = lastSelectedIndex; // Use index, not enum value
bannerOptions.bannerCallback = [optionsEnumArray, options](int selected) mutable -> void { bannerOptions.bannerCallback = [options](int selected) mutable -> void {
// Find the index of selected in optionsEnumArray // Find the index of selected in optionsEnumArray
int idx = 0; int idx = 0;
for (; idx < options; ++idx) { for (; idx < options; ++idx) {
@@ -1291,9 +1344,15 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
switch (menuQueue) { switch (menuQueue) {
case menu_none: case menu_none:
break; break;
case lora_Menu:
loraMenu();
break;
case lora_picker: case lora_picker:
LoraRegionPicker(); LoraRegionPicker();
break; break;
case device_role_picker:
DeviceRolePicker();
break;
case no_timeout_lora_picker: case no_timeout_lora_picker:
LoraRegionPicker(0); LoraRegionPicker(0);
break; break;
+3
View File
@@ -9,7 +9,9 @@ class menuHandler
public: public:
enum screenMenus { enum screenMenus {
menu_none, menu_none,
lora_Menu,
lora_picker, lora_picker,
device_role_picker,
no_timeout_lora_picker, no_timeout_lora_picker,
TZ_picker, TZ_picker,
twelve_hour_picker, twelve_hour_picker,
@@ -46,6 +48,7 @@ class menuHandler
static void OnboardMessage(); static void OnboardMessage();
static void LoraRegionPicker(uint32_t duration = 30000); static void LoraRegionPicker(uint32_t duration = 30000);
static void loraMenu(); static void loraMenu();
static void DeviceRolePicker();
static void handleMenuSwitch(OLEDDisplay *display); static void handleMenuSwitch(OLEDDisplay *display);
static void showConfirmationBanner(const char *message, std::function<void()> onConfirm); static void showConfirmationBanner(const char *message, std::function<void()> onConfirm);
static void clockMenu(); static void clockMenu();
+62 -13
View File
@@ -879,7 +879,26 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
config.display.heading_bold = false; config.display.heading_bold = false;
const char *displayLine = ""; // Initialize to empty string by default const char *displayLine = ""; // Initialize to empty string by default
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) { meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
bool usePhoneGPS = (ourNode && nodeDB->hasValidPosition(ourNode) &&
config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED);
if (usePhoneGPS) {
// Phone-provided GPS is active
displayLine = "Phone GPS";
int yOffset = (isHighResolution) ? 3 : 1;
if (isHighResolution) {
NodeListRenderer::drawScaledXBitmap16x16(x, getTextPositions(display)[line] + yOffset - 5, imgSatellite_width,
imgSatellite_height, imgSatellite, display);
} else {
display->drawXbm(x + 1, getTextPositions(display)[line] + yOffset, imgSatellite_width, imgSatellite_height,
imgSatellite);
}
int xOffset = (isHighResolution) ? 6 : 0;
display->drawString(x + 11 + xOffset, getTextPositions(display)[line++], displayLine);
} else if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
// GPS disabled / not present
if (config.position.fixed_position) { if (config.position.fixed_position) {
displayLine = "Fixed GPS"; displayLine = "Fixed GPS";
} else { } else {
@@ -896,6 +915,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
int xOffset = (isHighResolution) ? 6 : 0; int xOffset = (isHighResolution) ? 6 : 0;
display->drawString(x + 11 + xOffset, getTextPositions(display)[line++], displayLine); display->drawString(x + 11 + xOffset, getTextPositions(display)[line++], displayLine);
} else { } else {
// Onboard GPS
UIRenderer::drawGps(display, 0, getTextPositions(display)[line++], gpsStatus); UIRenderer::drawGps(display, 0, getTextPositions(display)[line++], gpsStatus);
} }
@@ -922,32 +942,61 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
// If GPS is off, no need to display these parts // If GPS is off, no need to display these parts
if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) { if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) {
// === Second Row: Last GPS Fix ===
if (gpsStatus->getLastFixMillis() > 0) {
uint32_t delta = (millis() - gpsStatus->getLastFixMillis()) / 1000; // seconds since last fix
uint32_t days = delta / 86400;
uint32_t hours = (delta % 86400) / 3600;
uint32_t mins = (delta % 3600) / 60;
uint32_t secs = delta % 60;
// === Second Row: Date === char buf[32];
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); #if defined(USE_EINK)
char datetimeStr[25]; // E-Ink: skip seconds, show only days/hours/mins
bool showTime = false; // set to true for full datetime if (days > 0) {
UIRenderer::formatDateTime(datetimeStr, sizeof(datetimeStr), rtc_sec, display, showTime); snprintf(buf, sizeof(buf), " Last: %ud %uh", days, hours);
char fullLine[40]; } else if (hours > 0) {
snprintf(fullLine, sizeof(fullLine), " Date: %s", datetimeStr); snprintf(buf, sizeof(buf), " Last: %uh %um", hours, mins);
display->drawString(0, getTextPositions(display)[line++], fullLine); } else {
snprintf(buf, sizeof(buf), " Last: %um", mins);
}
#else
// Non E-Ink: include seconds where useful
if (days > 0) {
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
} else if (hours > 0) {
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
} else if (mins > 0) {
snprintf(buf, sizeof(buf), "Last: %um %us", mins, secs);
} else {
snprintf(buf, sizeof(buf), "Last: %us", secs);
}
#endif
display->drawString(0, getTextPositions(display)[line++], buf);
} else {
display->drawString(0, getTextPositions(display)[line++], "Last: ?");
}
// === Third Row: Latitude === // === Third Row: Latitude ===
char latStr[32]; char latStr[32];
snprintf(latStr, sizeof(latStr), " Lat: %.5f", geoCoord.getLatitude() * 1e-7); snprintf(latStr, sizeof(latStr), "Lat: %.5f", geoCoord.getLatitude() * 1e-7);
display->drawString(x, getTextPositions(display)[line++], latStr); display->drawString(x, getTextPositions(display)[line++], latStr);
// === Fourth Row: Longitude === // === Fourth Row: Longitude ===
char lonStr[32]; char lonStr[32];
snprintf(lonStr, sizeof(lonStr), " Lon: %.5f", geoCoord.getLongitude() * 1e-7); snprintf(lonStr, sizeof(lonStr), "Lon: %.5f", geoCoord.getLongitude() * 1e-7);
display->drawString(x, getTextPositions(display)[line++], lonStr); display->drawString(x, getTextPositions(display)[line++], lonStr);
// === Fifth Row: Altitude === // === Fifth Row: Altitude ===
char DisplayLineTwo[32] = {0}; char DisplayLineTwo[32] = {0};
int32_t alt = (strcmp(displayLine, "Phone GPS") == 0 && ourNode && nodeDB->hasValidPosition(ourNode))
? ourNode->position.altitude
: geoCoord.getAltitude();
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), " Alt: %.0fft", geoCoord.getAltitude() * METERS_TO_FEET); snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), "Alt: %.0fft", geoCoord.getAltitude() * METERS_TO_FEET);
} else { } else {
snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), " Alt: %.0im", geoCoord.getAltitude()); snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), "Alt: %.0im", geoCoord.getAltitude());
} }
display->drawString(x, getTextPositions(display)[line++], DisplayLineTwo); display->drawString(x, getTextPositions(display)[line++], DisplayLineTwo);
} }
+1
View File
@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "NodeDB.h"
#include "graphics/Screen.h" #include "graphics/Screen.h"
#include "graphics/emotes.h" #include "graphics/emotes.h"
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
+72
View File
@@ -288,5 +288,77 @@ const uint8_t digital_icon_clock[] PROGMEM = {0b00111100, 0b01000010, 0b10000101
const uint8_t analog_icon_clock[] PROGMEM = {0b11111111, 0b01000010, 0b00100100, 0b00011000, const uint8_t analog_icon_clock[] PROGMEM = {0b11111111, 0b01000010, 0b00100100, 0b00011000,
0b00100100, 0b01000010, 0b01000010, 0b11111111}; 0b00100100, 0b01000010, 0b01000010, 0b11111111};
#define chirpy_width 38
#define chirpy_height 50
static unsigned char chirpy[] = {
0xfe, 0xff, 0xff, 0xff, 0xdf, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x80, 0xe3, 0x01,
0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0xc0, 0xe7, 0x01, 0x00, 0x00, 0x80, 0xe3, 0x01, 0x00,
0x00, 0x00, 0xe0, 0x81, 0xff, 0xff, 0x7f, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xcf, 0x7f,
0xfe, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc,
0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0,
0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0x87, 0x3f, 0xfc, 0xe0, 0xc1,
0x87, 0x3f, 0xfc, 0xe0, 0xc1, 0xcf, 0x7f, 0xfe, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0xc1, 0xff, 0xff, 0xff, 0xe0, 0x81, 0xff,
0xff, 0x7f, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0xc3, 0x00, 0xe0, 0x01, 0x00, 0xc3,
0x00, 0xe0, 0x01, 0x80, 0xe1, 0x01, 0xe0, 0x01, 0x80, 0xe1, 0x01, 0xe0, 0x01, 0xc0, 0x30, 0x03, 0xe0, 0x01, 0xc0, 0x30, 0x03,
0xe0, 0x01, 0x60, 0x18, 0x06, 0xe0, 0x01, 0x60, 0x18, 0x06, 0xe0, 0x01, 0x30, 0x0c, 0x0c, 0xe0, 0x01, 0x30, 0x0c, 0x0c, 0xe0,
0x01, 0x18, 0x06, 0x18, 0xe0, 0x01, 0x18, 0x06, 0x18, 0xe0, 0x01, 0x0c, 0x03, 0x30, 0xe0, 0x01, 0x0c, 0x03, 0x30, 0xe0, 0x01,
0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0xe0, 0xfe, 0xff, 0xff, 0xff, 0xdf};
#define chirpy_width_hirez 76
#define chirpy_height_hirez 100
static unsigned char chirpy_hirez[] = {
0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00,
0xfc, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc,
0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc, 0x03,
0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xf0, 0xff, 0x3f, 0xfc, 0xff, 0x00, 0xfc, 0x03, 0xf0,
0xff, 0xf0, 0xff, 0x3f, 0xfc, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f,
0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0,
0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff,
0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f,
0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0,
0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff,
0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00,
0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc,
0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03,
0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0,
0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f,
0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0,
0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0x3f, 0xc0, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xf0, 0xff,
0x3f, 0xfc, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xf0, 0xff, 0x3f, 0xfc, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0xfc, 0x03, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xfc, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f,
0x00, 0xfc, 0x03, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03,
0x00, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00,
0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xc0, 0x03, 0xfc, 0x03, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00,
0xc0, 0x03, 0xfc, 0x03, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xc0, 0x03, 0xfc, 0x03, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xc0,
0x03, 0xfc, 0x03, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xf0, 0x00,
0x0f, 0x0f, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x0f, 0x0f, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0xf0, 0x00, 0x0f,
0x0f, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x3c, 0xc0, 0x03, 0x3c, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x3c, 0xc0, 0x03, 0x3c,
0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x3c, 0xc0, 0x03, 0x3c, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x3c, 0xc0, 0x03, 0x3c, 0x00,
0x00, 0xfc, 0x03, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0xf0, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0xf0, 0x00, 0x00,
0xfc, 0x03, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0xf0, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0xf0, 0x00, 0x00, 0xfc,
0x03, 0x00, 0xc0, 0x03, 0x3c, 0x00, 0xc0, 0x03, 0x00, 0xfc, 0x03, 0x00, 0xc0, 0x03, 0x3c, 0x00, 0xc0, 0x03, 0x00, 0xfc, 0x03,
0x00, 0xc0, 0x03, 0x3c, 0x00, 0xc0, 0x03, 0x00, 0xfc, 0x03, 0x00, 0xc0, 0x03, 0x3c, 0x00, 0xc0, 0x03, 0x00, 0xfc, 0x03, 0x00,
0xf0, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0xfc, 0x03, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0xfc, 0x03, 0x00, 0xf0,
0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0xfc, 0x03, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xfc, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xf3, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3};
#define chirpy_small_image_width 8
#define chirpy_small_image_height 8
static unsigned char small_chirpy[] = {0x7f, 0x41, 0x55, 0x55, 0x55, 0x55, 0x41, 0x7f};
#include "img/icon.xbm" #include "img/icon.xbm"
static_assert(sizeof(icon_bits) >= 0, "Silence unused variable warning"); static_assert(sizeof(icon_bits) >= 0, "Silence unused variable warning");
+48 -48
View File
@@ -41,78 +41,78 @@ void tftSetup(void)
PacketAPI::create(PacketServer::init()); PacketAPI::create(PacketServer::init());
deviceScreen->init(new PacketClient); deviceScreen->init(new PacketClient);
#else #else
if (settingsMap[displayPanel] != no_screen) { if (portduino_config.displayPanel != no_screen) {
DisplayDriverConfig displayConfig; DisplayDriverConfig displayConfig;
static char *panels[] = {"NOSCREEN", "X11", "FB", "ST7789", "ST7735", "ST7735S", static char *panels[] = {"NOSCREEN", "X11", "FB", "ST7789", "ST7735", "ST7735S",
"ST7796", "ILI9341", "ILI9342", "ILI9486", "ILI9488", "HX8357D"}; "ST7796", "ILI9341", "ILI9342", "ILI9486", "ILI9488", "HX8357D"};
static char *touch[] = {"NOTOUCH", "XPT2046", "STMPE610", "GT911", "FT5x06"}; static char *touch[] = {"NOTOUCH", "XPT2046", "STMPE610", "GT911", "FT5x06"};
#if defined(USE_X11) #if defined(USE_X11)
if (settingsMap[displayPanel] == x11) { if (portduino_config.displayPanel == x11) {
if (settingsMap[displayWidth] && settingsMap[displayHeight]) if (portduino_config.displayWidth && portduino_config.displayHeight)
displayConfig = DisplayDriverConfig(DisplayDriverConfig::device_t::X11, (uint16_t)settingsMap[displayWidth], displayConfig = DisplayDriverConfig(DisplayDriverConfig::device_t::X11, (uint16_t)portduino_config.displayWidth,
(uint16_t)settingsMap[displayHeight]); (uint16_t)portduino_config.displayHeight);
else else
displayConfig.device(DisplayDriverConfig::device_t::X11); displayConfig.device(DisplayDriverConfig::device_t::X11);
} else } else
#elif defined(USE_FRAMEBUFFER) #elif defined(USE_FRAMEBUFFER)
if (settingsMap[displayPanel] == fb) { if (portduino_config.displayPanel == fb) {
if (settingsMap[displayWidth] && settingsMap[displayHeight]) if (portduino_config.displayWidth && portduino_config.displayHeight)
displayConfig = DisplayDriverConfig(DisplayDriverConfig::device_t::FB, (uint16_t)settingsMap[displayWidth], displayConfig = DisplayDriverConfig(DisplayDriverConfig::device_t::FB, (uint16_t)portduino_config.displayWidth,
(uint16_t)settingsMap[displayHeight]); (uint16_t)portduino_config.displayHeight);
else else
displayConfig.device(DisplayDriverConfig::device_t::FB); displayConfig.device(DisplayDriverConfig::device_t::FB);
} else } else
#endif #endif
{ {
displayConfig.device(DisplayDriverConfig::device_t::CUSTOM_TFT) displayConfig.device(DisplayDriverConfig::device_t::CUSTOM_TFT)
.panel(DisplayDriverConfig::panel_config_t{.type = panels[settingsMap[displayPanel]], .panel(DisplayDriverConfig::panel_config_t{.type = panels[portduino_config.displayPanel],
.panel_width = (uint16_t)settingsMap[displayWidth], .panel_width = (uint16_t)portduino_config.displayWidth,
.panel_height = (uint16_t)settingsMap[displayHeight], .panel_height = (uint16_t)portduino_config.displayHeight,
.rotation = (bool)settingsMap[displayRotate], .rotation = (bool)portduino_config.displayRotate,
.pin_cs = (int16_t)settingsMap[displayCS], .pin_cs = (int16_t)portduino_config.displayCS.pin,
.pin_rst = (int16_t)settingsMap[displayReset], .pin_rst = (int16_t)portduino_config.displayReset.pin,
.offset_x = (uint16_t)settingsMap[displayOffsetX], .offset_x = (uint16_t)portduino_config.displayOffsetX,
.offset_y = (uint16_t)settingsMap[displayOffsetY], .offset_y = (uint16_t)portduino_config.displayOffsetY,
.offset_rotation = (uint8_t)settingsMap[displayOffsetRotate], .offset_rotation = (uint8_t)portduino_config.displayOffsetRotate,
.invert = settingsMap[displayInvert] ? true : false, .invert = portduino_config.displayInvert ? true : false,
.rgb_order = (bool)settingsMap[displayRGBOrder], .rgb_order = (bool)portduino_config.displayRGBOrder,
.dlen_16bit = settingsMap[displayPanel] == ili9486 || .dlen_16bit = portduino_config.displayPanel == ili9486 ||
settingsMap[displayPanel] == ili9488}) portduino_config.displayPanel == ili9488})
.bus(DisplayDriverConfig::bus_config_t{.freq_write = (uint32_t)settingsMap[displayBusFrequency], .bus(DisplayDriverConfig::bus_config_t{.freq_write = (uint32_t)portduino_config.displayBusFrequency,
.freq_read = 16000000, .freq_read = 16000000,
.spi{.pin_dc = (int8_t)settingsMap[displayDC], .spi{.pin_dc = (int8_t)portduino_config.displayDC.pin,
.use_lock = true, .use_lock = true,
.spi_host = (uint16_t)settingsMap[displayspidev]}}) .spi_host = (uint16_t)portduino_config.display_spi_dev_int}})
.input(DisplayDriverConfig::input_config_t{.keyboardDevice = settingsStrings[keyboardDevice], .input(DisplayDriverConfig::input_config_t{.keyboardDevice = portduino_config.keyboardDevice,
.pointerDevice = settingsStrings[pointerDevice]}) .pointerDevice = portduino_config.pointerDevice})
.light(DisplayDriverConfig::light_config_t{.pin_bl = (int16_t)settingsMap[displayBacklight], .light(DisplayDriverConfig::light_config_t{.pin_bl = (int16_t)portduino_config.displayBacklight.pin,
.pwm_channel = (int8_t)settingsMap[displayBacklightPWMChannel], .pwm_channel = (int8_t)portduino_config.displayBacklightPWMChannel.pin,
.invert = (bool)settingsMap[displayBacklightInvert]}); .invert = (bool)portduino_config.displayBacklightInvert});
if (settingsMap[touchscreenI2CAddr] == -1) { if (portduino_config.touchscreenI2CAddr == -1) {
displayConfig.touch( displayConfig.touch(
DisplayDriverConfig::touch_config_t{.type = touch[settingsMap[touchscreenModule]], DisplayDriverConfig::touch_config_t{.type = touch[portduino_config.touchscreenModule],
.freq = (uint32_t)settingsMap[touchscreenBusFrequency], .freq = (uint32_t)portduino_config.touchscreenBusFrequency,
.pin_int = (int16_t)settingsMap[touchscreenIRQ], .pin_int = (int16_t)portduino_config.touchscreenIRQ.pin,
.offset_rotation = (uint8_t)settingsMap[touchscreenRotate], .offset_rotation = (uint8_t)portduino_config.touchscreenRotate,
.spi{ .spi{
.spi_host = (int8_t)settingsMap[touchscreenspidev], .spi_host = (int8_t)portduino_config.touchscreen_spi_dev_int,
}, },
.pin_cs = (int16_t)settingsMap[touchscreenCS]}); .pin_cs = (int16_t)portduino_config.touchscreenCS.pin});
} else { } else {
displayConfig.touch(DisplayDriverConfig::touch_config_t{ displayConfig.touch(DisplayDriverConfig::touch_config_t{
.type = touch[settingsMap[touchscreenModule]], .type = touch[portduino_config.touchscreenModule],
.freq = (uint32_t)settingsMap[touchscreenBusFrequency], .freq = (uint32_t)portduino_config.touchscreenBusFrequency,
.x_min = 0, .x_min = 0,
.x_max = .x_max = (int16_t)((portduino_config.touchscreenRotate & 1 ? portduino_config.displayWidth
(int16_t)((settingsMap[touchscreenRotate] & 1 ? settingsMap[displayWidth] : settingsMap[displayHeight]) - : portduino_config.displayHeight) -
1), 1),
.y_min = 0, .y_min = 0,
.y_max = .y_max = (int16_t)((portduino_config.touchscreenRotate & 1 ? portduino_config.displayHeight
(int16_t)((settingsMap[touchscreenRotate] & 1 ? settingsMap[displayHeight] : settingsMap[displayWidth]) - : portduino_config.displayWidth) -
1), 1),
.pin_int = (int16_t)settingsMap[touchscreenIRQ], .pin_int = (int16_t)portduino_config.touchscreenIRQ.pin,
.offset_rotation = (uint8_t)settingsMap[touchscreenRotate], .offset_rotation = (uint8_t)portduino_config.touchscreenRotate,
.i2c{.i2c_addr = (uint8_t)settingsMap[touchscreenI2CAddr]}}); .i2c{.i2c_addr = (uint8_t)portduino_config.touchscreenI2CAddr}});
} }
} }
deviceScreen = &DeviceScreen::create(&displayConfig); deviceScreen = &DeviceScreen::create(&displayConfig);
+2 -2
View File
@@ -33,9 +33,9 @@ int32_t LinuxInput::runOnce()
{ {
if (firstTime) { if (firstTime) {
if (settingsStrings[keyboardDevice] == "") if (portduino_config.keyboardDevice == "")
return disable(); return disable();
fd = open(settingsStrings[keyboardDevice].c_str(), O_RDWR); fd = open(portduino_config.keyboardDevice.c_str(), O_RDWR);
if (fd < 0) if (fd < 0)
return disable(); return disable();
ret = ioctl(fd, EVIOCGRAB, (void *)1); ret = ioctl(fd, EVIOCGRAB, (void *)1);
+2 -5
View File
@@ -40,10 +40,7 @@ bool RotaryEncoderImpl::init()
int32_t RotaryEncoderImpl::runOnce() int32_t RotaryEncoderImpl::runOnce()
{ {
InputEvent e; InputEvent e{originName, INPUT_BROKER_NONE, 0, 0, 0};
e.inputEvent = INPUT_BROKER_NONE;
e.source = this->originName;
static uint32_t lastPressed = millis(); static uint32_t lastPressed = millis();
if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) { if (rotary->readButton() == RotaryEncoder::ButtonState::BUTTON_PRESSED) {
if (lastPressed + 200 < millis()) { if (lastPressed + 200 < millis()) {
@@ -70,7 +67,7 @@ int32_t RotaryEncoderImpl::runOnce()
this->notifyObservers(&e); this->notifyObservers(&e);
} }
return 20; return 10;
} }
#endif #endif
+1 -1
View File
@@ -18,7 +18,7 @@ TouchScreenImpl1::TouchScreenImpl1(uint16_t width, uint16_t height, bool (*getTo
void TouchScreenImpl1::init() void TouchScreenImpl1::init()
{ {
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (settingsMap[touchscreenModule]) { if (portduino_config.touchscreenModule) {
TouchScreenBase::init(true); TouchScreenBase::init(true);
inputBroker->registerSource(this); inputBroker->registerSource(this);
} else { } else {
+1 -1
View File
@@ -6,7 +6,7 @@
#ifndef TB_DIRECTION #ifndef TB_DIRECTION
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
#include "PortduinoGlue.h" #include "PortduinoGlue.h"
#define TB_DIRECTION (PinStatus) settingsMap[tbDirection] #define TB_DIRECTION (PinStatus) portduino_config.lora_usb_vid
#else #else
#define TB_DIRECTION RISING #define TB_DIRECTION RISING
#endif #endif
+4
View File
@@ -13,7 +13,11 @@ void CardKbI2cImpl::init()
if (cardkb_found.address == 0x00) { if (cardkb_found.address == 0x00) {
LOG_DEBUG("Rescan for I2C keyboard"); LOG_DEBUG("Rescan for I2C keyboard");
uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR, MPR121_KB_ADDR, TCA8418_KB_ADDR}; uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR, MPR121_KB_ADDR, TCA8418_KB_ADDR};
#if defined(T_LORA_PAGER)
uint8_t i2caddr_asize = sizeof(i2caddr_scan) / sizeof(i2caddr_scan[0]); uint8_t i2caddr_asize = sizeof(i2caddr_scan) / sizeof(i2caddr_scan[0]);
#else
uint8_t i2caddr_asize = 5;
#endif
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire()); auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
#if WIRE_INTERFACES_COUNT == 2 #if WIRE_INTERFACES_COUNT == 2
+54 -55
View File
@@ -390,7 +390,7 @@ void setup()
concurrency::hasBeenSetup = true; concurrency::hasBeenSetup = true;
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
SPISettings spiSettings(settingsMap[spiSpeed], MSBFIRST, SPI_MODE0); SPISettings spiSettings(portduino_config.spiSpeed, MSBFIRST, SPI_MODE0);
#else #else
SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0); SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
#endif #endif
@@ -535,9 +535,9 @@ void setup()
#elif defined(I2C_SDA) && !defined(ARCH_RP2040) #elif defined(I2C_SDA) && !defined(ARCH_RP2040)
Wire.begin(I2C_SDA, I2C_SCL); Wire.begin(I2C_SDA, I2C_SCL);
#elif defined(ARCH_PORTDUINO) #elif defined(ARCH_PORTDUINO)
if (settingsStrings[i2cdev] != "") { if (portduino_config.i2cdev != "") {
LOG_INFO("Use %s as I2C device", settingsStrings[i2cdev].c_str()); LOG_INFO("Use %s as I2C device", portduino_config.i2cdev.c_str());
Wire.begin(settingsStrings[i2cdev].c_str()); Wire.begin(portduino_config.i2cdev.c_str());
} else { } else {
LOG_INFO("No I2C device configured, Skip"); LOG_INFO("No I2C device configured, Skip");
} }
@@ -583,7 +583,7 @@ void setup()
#if defined(I2C_SDA) #if defined(I2C_SDA)
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE); i2cScanner->scanPort(ScanI2C::I2CPort::WIRE);
#elif defined(ARCH_PORTDUINO) #elif defined(ARCH_PORTDUINO)
if (settingsStrings[i2cdev] != "") { if (portduino_config.i2cdev != "") {
LOG_INFO("Scan for i2c devices"); LOG_INFO("Scan for i2c devices");
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE); i2cScanner->scanPort(ScanI2C::I2CPort::WIRE);
} }
@@ -743,6 +743,7 @@ void setup()
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RAK12035, meshtastic_TelemetrySensorType_RAK12035); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::RAK12035, meshtastic_TelemetrySensorType_RAK12035);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PCT2075, meshtastic_TelemetrySensorType_PCT2075); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::PCT2075, meshtastic_TelemetrySensorType_PCT2075);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SCD4X, meshtastic_TelemetrySensorType_SCD4X); scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::SCD4X, meshtastic_TelemetrySensorType_SCD4X);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::TSL2561, meshtastic_TelemetrySensorType_TSL2561);
i2cScanner.reset(); i2cScanner.reset();
#endif #endif
@@ -855,7 +856,7 @@ void setup()
SPI.begin(false); SPI.begin(false);
#endif // HW_SPI1_DEVICE #endif // HW_SPI1_DEVICE
#elif ARCH_PORTDUINO #elif ARCH_PORTDUINO
if (settingsStrings[spidev] != "ch341") { if (portduino_config.lora_spi_dev != "ch341") {
SPI.begin(); SPI.begin();
} }
#elif !defined(ARCH_ESP32) // ARCH_RP2040 #elif !defined(ARCH_ESP32) // ARCH_RP2040
@@ -881,7 +882,7 @@ void setup()
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS)
screen = new graphics::Screen(screen_found, screen_model, screen_geometry); screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
#elif defined(ARCH_PORTDUINO) #elif defined(ARCH_PORTDUINO)
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) && if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || portduino_config.displayPanel) &&
config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
screen = new graphics::Screen(screen_found, screen_model, screen_geometry); screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
} }
@@ -982,13 +983,13 @@ void setup()
#endif #endif
#if defined(ARCH_PORTDUINO) #if defined(ARCH_PORTDUINO)
if (settingsMap.count(userButtonPin) != 0 && settingsMap[userButtonPin] != RADIOLIB_NC) { if (portduino_config.userButtonPin.enabled) {
LOG_DEBUG("Use GPIO%02d for button", settingsMap[userButtonPin]); LOG_DEBUG("Use GPIO%02d for button", portduino_config.userButtonPin.pin);
UserButtonThread = new ButtonThread("UserButton"); UserButtonThread = new ButtonThread("UserButton");
if (screen) { if (screen) {
ButtonConfig config; ButtonConfig config;
config.pinNumber = (uint8_t)settingsMap[userButtonPin]; config.pinNumber = (uint8_t)portduino_config.userButtonPin.pin;
config.activeLow = true; config.activeLow = true;
config.activePullup = true; config.activePullup = true;
config.pullupSense = INPUT_PULLUP; config.pullupSense = INPUT_PULLUP;
@@ -1145,7 +1146,7 @@ void setup()
if (screen) if (screen)
screen->setup(); screen->setup();
#elif defined(ARCH_PORTDUINO) #elif defined(ARCH_PORTDUINO)
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) && if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || portduino_config.displayPanel) &&
config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) { config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
screen->setup(); screen->setup();
} }
@@ -1161,15 +1162,10 @@ void setup()
#endif #endif
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
const struct {
configNames cfgName;
std::string strName;
} loraModules[] = {{use_rf95, "RF95"}, {use_sx1262, "sx1262"}, {use_sx1268, "sx1268"}, {use_sx1280, "sx1280"},
{use_lr1110, "lr1110"}, {use_lr1120, "lr1120"}, {use_lr1121, "lr1121"}, {use_llcc68, "LLCC68"}};
// as one can't use a function pointer to the class constructor: // as one can't use a function pointer to the class constructor:
auto loraModuleInterface = [](configNames cfgName, LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, auto loraModuleInterface = [](LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) { RADIOLIB_PIN_TYPE busy) {
switch (cfgName) { switch (portduino_config.lora_module) {
case use_rf95: case use_rf95:
return (RadioInterface *)new RF95Interface(hal, cs, irq, rst, busy); return (RadioInterface *)new RF95Interface(hal, cs, irq, rst, busy);
case use_sx1262: case use_sx1262:
@@ -1186,31 +1182,34 @@ void setup()
return (RadioInterface *)new LR1121Interface(hal, cs, irq, rst, busy); return (RadioInterface *)new LR1121Interface(hal, cs, irq, rst, busy);
case use_llcc68: case use_llcc68:
return (RadioInterface *)new LLCC68Interface(hal, cs, irq, rst, busy); return (RadioInterface *)new LLCC68Interface(hal, cs, irq, rst, busy);
case use_simradio:
return (RadioInterface *)new SimRadio;
default: default:
assert(0); // shouldn't happen assert(0); // shouldn't happen
return (RadioInterface *)nullptr; return (RadioInterface *)nullptr;
} }
}; };
for (auto &loraModule : loraModules) {
if (settingsMap[loraModule.cfgName] && !rIf) { LOG_DEBUG("Activate %s radio on SPI port %s", portduino_config.loraModules[portduino_config.lora_module].c_str(),
LOG_DEBUG("Activate %s radio on SPI port %s", loraModule.strName.c_str(), settingsStrings[spidev].c_str()); portduino_config.lora_spi_dev.c_str());
if (settingsStrings[spidev] == "ch341") { if (portduino_config.lora_spi_dev == "ch341") {
RadioLibHAL = ch341Hal; RadioLibHAL = ch341Hal;
} else { } else {
RadioLibHAL = new LockingArduinoHal(SPI, spiSettings); RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
}
rIf = loraModuleInterface(loraModule.cfgName, (LockingArduinoHal *)RadioLibHAL, settingsMap[cs_pin],
settingsMap[irq_pin], settingsMap[reset_pin], settingsMap[busy_pin]);
if (!rIf->init()) {
LOG_WARN("No %s radio", loraModule.strName.c_str());
delete rIf;
rIf = NULL;
exit(EXIT_FAILURE);
} else {
LOG_INFO("%s init success", loraModule.strName.c_str());
}
}
} }
rIf =
loraModuleInterface((LockingArduinoHal *)RadioLibHAL, portduino_config.lora_cs_pin.pin, portduino_config.lora_irq_pin.pin,
portduino_config.lora_reset_pin.pin, portduino_config.lora_busy_pin.pin);
if (!rIf->init()) {
LOG_WARN("No %s radio", portduino_config.loraModules[portduino_config.lora_module].c_str());
delete rIf;
rIf = NULL;
exit(EXIT_FAILURE);
} else {
LOG_INFO("%s init success", portduino_config.loraModules[portduino_config.lora_module].c_str());
}
#elif defined(HW_SPI1_DEVICE) #elif defined(HW_SPI1_DEVICE)
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI1, spiSettings); LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI1, spiSettings);
#else // HW_SPI1_DEVICE #else // HW_SPI1_DEVICE
@@ -1232,20 +1231,6 @@ void setup()
} }
#endif #endif
#if defined(ARCH_PORTDUINO)
if (!rIf) {
rIf = new SimRadio;
if (!rIf->init()) {
LOG_WARN("No simulated radio");
delete rIf;
rIf = NULL;
} else {
LOG_INFO("Use SIMULATED radio!");
radioType = SIM_RADIO;
}
}
#endif
#if defined(RF95_IRQ) && RADIOLIB_EXCLUDE_SX127X != 1 #if defined(RF95_IRQ) && RADIOLIB_EXCLUDE_SX127X != 1
if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) { if ((!rIf) && (config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24)) {
rIf = new RF95Interface(RadioLibHAL, LORA_CS, RF95_IRQ, RF95_RESET, RF95_DIO1); rIf = new RF95Interface(RadioLibHAL, LORA_CS, RF95_IRQ, RF95_RESET, RF95_DIO1);
@@ -1460,7 +1445,7 @@ void setup()
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
#if __has_include(<ulfius.h>) #if __has_include(<ulfius.h>)
if (settingsMap[webserverport] != -1) { if (portduino_config.webserverport != -1) {
piwebServerThread = new PiWebServerThread(); piwebServerThread = new PiWebServerThread();
std::atexit([] { delete piwebServerThread; }); std::atexit([] { delete piwebServerThread; });
} }
@@ -1521,6 +1506,9 @@ extern meshtastic_DeviceMetadata getDeviceMetadata()
deviceMetadata.hw_model = HW_VENDOR; deviceMetadata.hw_model = HW_VENDOR;
deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled; deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled;
deviceMetadata.excluded_modules = meshtastic_ExcludedModules_EXCLUDED_NONE; deviceMetadata.excluded_modules = meshtastic_ExcludedModules_EXCLUDED_NONE;
#if MESHTASTIC_EXCLUDE_MQTT
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_MQTT_CONFIG;
#endif
#if MESHTASTIC_EXCLUDE_REMOTEHARDWARE #if MESHTASTIC_EXCLUDE_REMOTEHARDWARE
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_REMOTEHARDWARE_CONFIG; deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_REMOTEHARDWARE_CONFIG;
#endif #endif
@@ -1543,10 +1531,21 @@ extern meshtastic_DeviceMetadata getDeviceMetadata()
#if NO_EXT_GPIO && NO_GPS || MESHTASTIC_EXCLUDE_SERIAL #if NO_EXT_GPIO && NO_GPS || MESHTASTIC_EXCLUDE_SERIAL
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_SERIAL_CONFIG; deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_SERIAL_CONFIG;
#endif #endif
#ifndef ARCH_ESP32 #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_PAXCOUNTER
// PAXCOUNTER is only supported on ESP32 due to memory constraints
#else
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_PAXCOUNTER_CONFIG; deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_PAXCOUNTER_CONFIG;
#endif #endif
#if !defined(HAS_RGB_LED) && !RAK_4631 #if MESHTASTIC_EXCLUDE_STOREFORWARD
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_STOREFORWARD_CONFIG;
#endif
#if MESHTASTIC_EXCLUDE_RANGETEST
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_RANGETEST_CONFIG;
#endif
#if MESHTASTIC_EXCLUDE_NEIGHBORINFO
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_NEIGHBORINFO_CONFIG;
#endif
#if (!defined(HAS_RGB_LED) && !defined(RAK_4631)) || defined(MESHTASTIC_EXCLUDE_AMBIENTLIGHTING)
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_AMBIENTLIGHTING_CONFIG; deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_AMBIENTLIGHTING_CONFIG;
#endif #endif
+3 -3
View File
@@ -21,7 +21,7 @@ static const Module::RfSwitchMode_t rfswitch_table[] = {
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not // Particular boards might define a different max power based on what their hardware can do, default to max power output if not
// specified (may be dangerous if using external PA and LR11x0 power config forgotten) // specified (may be dangerous if using external PA and LR11x0 power config forgotten)
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
#define LR1110_MAX_POWER settingsMap[lr1110_max_power] #define LR1110_MAX_POWER portduino_config.lr1110_max_power
#endif #endif
#ifndef LR1110_MAX_POWER #ifndef LR1110_MAX_POWER
#define LR1110_MAX_POWER 22 #define LR1110_MAX_POWER 22
@@ -30,7 +30,7 @@ static const Module::RfSwitchMode_t rfswitch_table[] = {
// the 2.4G part maxes at 13dBm // the 2.4G part maxes at 13dBm
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
#define LR1120_MAX_POWER settingsMap[lr1120_max_power] #define LR1120_MAX_POWER portduino_config.lr1120_max_power
#endif #endif
#ifndef LR1120_MAX_POWER #ifndef LR1120_MAX_POWER
#define LR1120_MAX_POWER 13 #define LR1120_MAX_POWER 13
@@ -55,7 +55,7 @@ template <typename T> bool LR11x0Interface<T>::init()
#endif #endif
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
float tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; float tcxoVoltage = (float)portduino_config.dio3_tcxo_voltage / 1000;
// FIXME: correct logic to default to not using TCXO if no voltage is specified for LR11x0_DIO3_TCXO_VOLTAGE // FIXME: correct logic to default to not using TCXO if no voltage is specified for LR11x0_DIO3_TCXO_VOLTAGE
#elif !defined(LR11X0_DIO3_TCXO_VOLTAGE) #elif !defined(LR11X0_DIO3_TCXO_VOLTAGE)
float tcxoVoltage = float tcxoVoltage =
+9 -5
View File
@@ -34,8 +34,11 @@ bool NextHopRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
bool weWereNextHop = false; bool weWereNextHop = false;
if (wasSeenRecently(p, true, &wasFallback, &weWereNextHop)) { // Note: this will also add a recent packet record if (wasSeenRecently(p, true, &wasFallback, &weWereNextHop)) { // Note: this will also add a recent packet record
printPacket("Ignore dupe incoming msg", p); printPacket("Ignore dupe incoming msg", p);
rxDupe++;
stopRetransmission(p->from, p->id); if (p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA) {
rxDupe++;
stopRetransmission(p->from, p->id);
}
// If it was a fallback to flooding, try to relay again // If it was a fallback to flooding, try to relay again
if (wasFallback) { if (wasFallback) {
@@ -71,10 +74,11 @@ void NextHopRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtast
if (p->from != 0) { if (p->from != 0) {
meshtastic_NodeInfoLite *origTx = nodeDB->getMeshNode(p->from); meshtastic_NodeInfoLite *origTx = nodeDB->getMeshNode(p->from);
if (origTx) { if (origTx) {
// Either relayer of ACK was also a relayer of the packet, or we were the relayer and the ACK came directly from // Either relayer of ACK was also a relayer of the packet, or we were the *only* relayer and the ACK came directly
// the destination // from the destination
if (wasRelayer(p->relay_node, p->decoded.request_id, p->to) || if (wasRelayer(p->relay_node, p->decoded.request_id, p->to) ||
(wasRelayer(ourRelayID, p->decoded.request_id, p->to) && p->hop_start != 0 && p->hop_start == p->hop_limit)) { (p->hop_start != 0 && p->hop_start == p->hop_limit &&
wasSoleRelayer(ourRelayID, p->decoded.request_id, p->to))) {
if (origTx->next_hop != p->relay_node) { // Not already set if (origTx->next_hop != p->relay_node) { // Not already set
LOG_INFO("Update next hop of 0x%x to 0x%x based on ACK/reply", p->from, p->relay_node); LOG_INFO("Update next hop of 0x%x to 0x%x based on ACK/reply", p->from, p->relay_node);
origTx->next_hop = p->relay_node; origTx->next_hop = p->relay_node;
+23 -3
View File
@@ -673,7 +673,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
#endif #endif
#elif ARCH_PORTDUINO #elif ARCH_PORTDUINO
bool hasScreen = false; bool hasScreen = false;
if (settingsMap[displayPanel]) if (portduino_config.displayPanel)
hasScreen = true; hasScreen = true;
else else
hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C; hasScreen = screen_found.port != ScanI2C::I2CPort::NO_I2C;
@@ -775,7 +775,9 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.version = DEVICESTATE_CUR_VER; moduleConfig.version = DEVICESTATE_CUR_VER;
moduleConfig.has_mqtt = true; moduleConfig.has_mqtt = true;
#if !MESHTASTIC_EXCLUDE_RANGETEST
moduleConfig.has_range_test = true; moduleConfig.has_range_test = true;
#endif
moduleConfig.has_serial = true; moduleConfig.has_serial = true;
moduleConfig.has_store_forward = true; moduleConfig.has_store_forward = true;
moduleConfig.has_telemetry = true; moduleConfig.has_telemetry = true;
@@ -841,6 +843,12 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.canned_message.inputbroker_event_press = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT; moduleConfig.canned_message.inputbroker_event_press = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
#endif #endif
moduleConfig.has_canned_message = true; moduleConfig.has_canned_message = true;
#if !MESHTASTIC_EXCLUDE_AUDIO
moduleConfig.has_audio = true;
#endif
#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
moduleConfig.has_paxcounter = true;
#endif
#if USERPREFS_MQTT_ENABLED && !MESHTASTIC_EXCLUDE_MQTT #if USERPREFS_MQTT_ENABLED && !MESHTASTIC_EXCLUDE_MQTT
moduleConfig.mqtt.enabled = true; moduleConfig.mqtt.enabled = true;
#endif #endif
@@ -883,12 +891,14 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.detection_sensor.detection_trigger_type = meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH; moduleConfig.detection_sensor.detection_trigger_type = meshtastic_ModuleConfig_DetectionSensorConfig_TriggerType_LOGIC_HIGH;
moduleConfig.detection_sensor.minimum_broadcast_secs = 45; moduleConfig.detection_sensor.minimum_broadcast_secs = 45;
#if !MESHTASTIC_EXCLUDE_AMBIENTLIGHTING
moduleConfig.has_ambient_lighting = true; moduleConfig.has_ambient_lighting = true;
moduleConfig.ambient_lighting.current = 10; moduleConfig.ambient_lighting.current = 10;
// Default to a color based on our node number // Default to a color based on our node number
moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16; moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8; moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF; moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
#endif
initModuleConfigIntervals(); initModuleConfigIntervals();
} }
@@ -1334,8 +1344,8 @@ void NodeDB::loadFromDisk()
} }
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
// set any config overrides // set any config overrides
if (settingsMap[has_configDisplayMode]) { if (portduino_config.has_configDisplayMode) {
config.display.displaymode = (_meshtastic_Config_DisplayConfig_DisplayMode)settingsMap[configDisplayMode]; config.display.displaymode = (_meshtastic_Config_DisplayConfig_DisplayMode)portduino_config.configDisplayMode;
} }
#endif #endif
@@ -1428,15 +1438,25 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat)
moduleConfig.has_canned_message = true; moduleConfig.has_canned_message = true;
moduleConfig.has_external_notification = true; moduleConfig.has_external_notification = true;
moduleConfig.has_mqtt = true; moduleConfig.has_mqtt = true;
#if !MESHTASTIC_EXCLUDE_RANGETEST
moduleConfig.has_range_test = true; moduleConfig.has_range_test = true;
#endif
moduleConfig.has_serial = true; moduleConfig.has_serial = true;
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
moduleConfig.has_store_forward = true; moduleConfig.has_store_forward = true;
#endif
moduleConfig.has_telemetry = true; moduleConfig.has_telemetry = true;
moduleConfig.has_neighbor_info = true; moduleConfig.has_neighbor_info = true;
moduleConfig.has_detection_sensor = true; moduleConfig.has_detection_sensor = true;
#if !MESHTASTIC_EXCLUDE_AMBIENTLIGHTING
moduleConfig.has_ambient_lighting = true; moduleConfig.has_ambient_lighting = true;
#endif
#if !MESHTASTIC_EXCLUDE_AUDIO
moduleConfig.has_audio = true; moduleConfig.has_audio = true;
#endif
#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
moduleConfig.has_paxcounter = true; moduleConfig.has_paxcounter = true;
#endif
success &= success &=
saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig); saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig);
+25 -10
View File
@@ -294,7 +294,7 @@ void PacketHistory::insert(const PacketRecord &r)
/* Check if a certain node was a relayer of a packet in the history given an ID and sender /* Check if a certain node was a relayer of a packet in the history given an ID and sender
* @return true if node was indeed a relayer, false if not */ * @return true if node was indeed a relayer, false if not */
bool PacketHistory::wasRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender) bool PacketHistory::wasRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender, bool *wasSole)
{ {
if (!initOk()) { if (!initOk()) {
LOG_ERROR("PacketHistory - wasRelayer: NOT INITIALIZED!"); LOG_ERROR("PacketHistory - wasRelayer: NOT INITIALIZED!");
@@ -322,27 +322,42 @@ bool PacketHistory::wasRelayer(const uint8_t relayer, const uint32_t id, const N
found->sender, found->id, found->next_hop, millis() - found->rxTimeMsec, found->relayed_by[0], found->relayed_by[1], found->sender, found->id, found->next_hop, millis() - found->rxTimeMsec, found->relayed_by[0], found->relayed_by[1],
found->relayed_by[2], relayer); found->relayed_by[2], relayer);
#endif #endif
return wasRelayer(relayer, *found); return wasRelayer(relayer, *found, wasSole);
} }
/* Check if a certain node was a relayer of a packet in the history given iterator /* Check if a certain node was a relayer of a packet in the history given iterator
* @return true if node was indeed a relayer, false if not */ * @return true if node was indeed a relayer, false if not */
bool PacketHistory::wasRelayer(const uint8_t relayer, const PacketRecord &r) bool PacketHistory::wasRelayer(const uint8_t relayer, const PacketRecord &r, bool *wasSole)
{ {
for (uint8_t i = 0; i < NUM_RELAYERS; i++) { bool found = false;
bool other_present = false;
for (uint8_t i = 0; i < NUM_RELAYERS; ++i) {
if (r.relayed_by[i] == relayer) { if (r.relayed_by[i] == relayer) {
#if VERBOSE_PACKET_HISTORY found = true;
LOG_DEBUG("Packet History - was rel.PR.: s=%08x id=%08x rls=%02x %02x %02x / rl=%02x? YES", r.sender, r.id, } else if (r.relayed_by[i] != 0) {
r.relayed_by[0], r.relayed_by[1], r.relayed_by[2], relayer); other_present = true;
#endif
return true;
} }
} }
if (wasSole) {
*wasSole = (found && !other_present);
}
#if VERBOSE_PACKET_HISTORY #if VERBOSE_PACKET_HISTORY
LOG_DEBUG("Packet History - was rel.PR.: s=%08x id=%08x rls=%02x %02x %02x / rl=%02x? NO", r.sender, r.id, r.relayed_by[0], LOG_DEBUG("Packet History - was rel.PR.: s=%08x id=%08x rls=%02x %02x %02x / rl=%02x? NO", r.sender, r.id, r.relayed_by[0],
r.relayed_by[1], r.relayed_by[2], relayer); r.relayed_by[1], r.relayed_by[2], relayer);
#endif #endif
return false;
return found;
}
// Check if a certain node was the *only* relayer of a packet in the history given an ID and sender
bool PacketHistory::wasSoleRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender)
{
bool wasSole = false;
wasRelayer(relayer, id, sender, &wasSole);
return wasSole;
} }
// Remove a relayer from the list of relayers of a packet in the history given an ID and sender // Remove a relayer from the list of relayers of a packet in the history given an ID and sender
+7 -2
View File
@@ -34,8 +34,9 @@ class PacketHistory
void insert(const PacketRecord &r); // Insert or replace a packet record in the history void insert(const PacketRecord &r); // Insert or replace a packet record in the history
/* Check if a certain node was a relayer of a packet in the history given iterator /* Check if a certain node was a relayer of a packet in the history given iterator
* If wasSole is not nullptr, it will be set to true if the relayer was the only relayer of that packet
* @return true if node was indeed a relayer, false if not */ * @return true if node was indeed a relayer, false if not */
bool wasRelayer(const uint8_t relayer, const PacketRecord &r); bool wasRelayer(const uint8_t relayer, const PacketRecord &r, bool *wasSole = nullptr);
PacketHistory(const PacketHistory &); // non construction-copyable PacketHistory(const PacketHistory &); // non construction-copyable
PacketHistory &operator=(const PacketHistory &); // non copyable PacketHistory &operator=(const PacketHistory &); // non copyable
@@ -54,8 +55,12 @@ class PacketHistory
bool *weWereNextHop = nullptr); bool *weWereNextHop = nullptr);
/* Check if a certain node was a relayer of a packet in the history given an ID and sender /* Check if a certain node was a relayer of a packet in the history given an ID and sender
* If wasSole is not nullptr, it will be set to true if the relayer was the only relayer of that packet
* @return true if node was indeed a relayer, false if not */ * @return true if node was indeed a relayer, false if not */
bool wasRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender); bool wasRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender, bool *wasSole = nullptr);
// Check if a certain node was the *only* relayer of a packet in the history given an ID and sender
bool wasSoleRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender);
// Remove a relayer from the list of relayers of a packet in the history given an ID and sender // Remove a relayer from the list of relayers of a packet in the history given an ID and sender
void removeRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender); void removeRelayer(const uint8_t relayer, const uint32_t id, const NodeNum sender);
+45
View File
@@ -34,6 +34,21 @@
// Flag to indicate a heartbeat was received and we should send queue status // Flag to indicate a heartbeat was received and we should send queue status
bool heartbeatReceived = false; bool heartbeatReceived = false;
// Helper function to skip excluded module configs and advance state
size_t PhoneAPI::skipExcludedModuleConfig(uint8_t *buf)
{
config_state++;
if (config_state > (_meshtastic_AdminMessage_ModuleConfigType_MAX + 1)) {
if (config_nonce == SPECIAL_NONCE_ONLY_CONFIG) {
state = STATE_SEND_FILEMANIFEST;
} else {
state = STATE_SEND_OTHER_NODEINFOS;
}
config_state = 0;
}
return getFromRadio(buf);
}
PhoneAPI::PhoneAPI() PhoneAPI::PhoneAPI()
{ {
lastContactMsec = millis(); lastContactMsec = millis();
@@ -354,20 +369,35 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.moduleConfig.payload_variant.serial = moduleConfig.serial; fromRadioScratch.moduleConfig.payload_variant.serial = moduleConfig.serial;
break; break;
case meshtastic_ModuleConfig_external_notification_tag: case meshtastic_ModuleConfig_external_notification_tag:
#if !(NO_EXT_GPIO || MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION)
LOG_DEBUG("Send module config: ext notification"); LOG_DEBUG("Send module config: ext notification");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_external_notification_tag; fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_external_notification_tag;
fromRadioScratch.moduleConfig.payload_variant.external_notification = moduleConfig.external_notification; fromRadioScratch.moduleConfig.payload_variant.external_notification = moduleConfig.external_notification;
break; break;
#else
LOG_DEBUG("External Notification module excluded from build, skipping");
return skipExcludedModuleConfig(buf);
#endif
case meshtastic_ModuleConfig_store_forward_tag: case meshtastic_ModuleConfig_store_forward_tag:
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
LOG_DEBUG("Send module config: store forward"); LOG_DEBUG("Send module config: store forward");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_store_forward_tag; fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_store_forward_tag;
fromRadioScratch.moduleConfig.payload_variant.store_forward = moduleConfig.store_forward; fromRadioScratch.moduleConfig.payload_variant.store_forward = moduleConfig.store_forward;
break; break;
#else
LOG_DEBUG("Store & Forward module excluded from build, skipping");
return skipExcludedModuleConfig(buf);
#endif
case meshtastic_ModuleConfig_range_test_tag: case meshtastic_ModuleConfig_range_test_tag:
#if !MESHTASTIC_EXCLUDE_RANGETEST
LOG_DEBUG("Send module config: range test"); LOG_DEBUG("Send module config: range test");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_range_test_tag; fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_range_test_tag;
fromRadioScratch.moduleConfig.payload_variant.range_test = moduleConfig.range_test; fromRadioScratch.moduleConfig.payload_variant.range_test = moduleConfig.range_test;
break; break;
#else
LOG_DEBUG("Range Test module excluded from build, skipping");
return skipExcludedModuleConfig(buf);
#endif
case meshtastic_ModuleConfig_telemetry_tag: case meshtastic_ModuleConfig_telemetry_tag:
LOG_DEBUG("Send module config: telemetry"); LOG_DEBUG("Send module config: telemetry");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_telemetry_tag; fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_telemetry_tag;
@@ -379,10 +409,15 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.moduleConfig.payload_variant.canned_message = moduleConfig.canned_message; fromRadioScratch.moduleConfig.payload_variant.canned_message = moduleConfig.canned_message;
break; break;
case meshtastic_ModuleConfig_audio_tag: case meshtastic_ModuleConfig_audio_tag:
#if !MESHTASTIC_EXCLUDE_AUDIO
LOG_DEBUG("Send module config: audio"); LOG_DEBUG("Send module config: audio");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_audio_tag; fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_audio_tag;
fromRadioScratch.moduleConfig.payload_variant.audio = moduleConfig.audio; fromRadioScratch.moduleConfig.payload_variant.audio = moduleConfig.audio;
break; break;
#else
LOG_DEBUG("Audio module excluded from build, skipping");
return skipExcludedModuleConfig(buf);
#endif
case meshtastic_ModuleConfig_remote_hardware_tag: case meshtastic_ModuleConfig_remote_hardware_tag:
LOG_DEBUG("Send module config: remote hardware"); LOG_DEBUG("Send module config: remote hardware");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_remote_hardware_tag; fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_remote_hardware_tag;
@@ -399,15 +434,25 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.moduleConfig.payload_variant.detection_sensor = moduleConfig.detection_sensor; fromRadioScratch.moduleConfig.payload_variant.detection_sensor = moduleConfig.detection_sensor;
break; break;
case meshtastic_ModuleConfig_ambient_lighting_tag: case meshtastic_ModuleConfig_ambient_lighting_tag:
#if !MESHTASTIC_EXCLUDE_AMBIENTLIGHTING
LOG_DEBUG("Send module config: ambient lighting"); LOG_DEBUG("Send module config: ambient lighting");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_ambient_lighting_tag; fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_ambient_lighting_tag;
fromRadioScratch.moduleConfig.payload_variant.ambient_lighting = moduleConfig.ambient_lighting; fromRadioScratch.moduleConfig.payload_variant.ambient_lighting = moduleConfig.ambient_lighting;
break; break;
#else
LOG_DEBUG("Ambient Lighting module excluded from build, skipping");
return skipExcludedModuleConfig(buf);
#endif
case meshtastic_ModuleConfig_paxcounter_tag: case meshtastic_ModuleConfig_paxcounter_tag:
#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
LOG_DEBUG("Send module config: paxcounter"); LOG_DEBUG("Send module config: paxcounter");
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_paxcounter_tag; fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_paxcounter_tag;
fromRadioScratch.moduleConfig.payload_variant.paxcounter = moduleConfig.paxcounter; fromRadioScratch.moduleConfig.payload_variant.paxcounter = moduleConfig.paxcounter;
break; break;
#else
LOG_DEBUG("Paxcounter module excluded from build, skipping");
return skipExcludedModuleConfig(buf);
#endif
default: default:
LOG_ERROR("Unknown module config type %d", config_state); LOG_ERROR("Unknown module config type %d", config_state);
} }
+3
View File
@@ -172,4 +172,7 @@ class PhoneAPI
/// If the mesh service tells us fromNum has changed, tell the phone /// If the mesh service tells us fromNum has changed, tell the phone
virtual int onNotify(uint32_t newValue) override; virtual int onNotify(uint32_t newValue) override;
/// Helper function to skip excluded module configs and advance state
size_t skipExcludedModuleConfig(uint8_t *buf);
}; };
+11 -11
View File
@@ -10,7 +10,7 @@
#endif #endif
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
#define RF95_MAX_POWER settingsMap[rf95_max_power] #define RF95_MAX_POWER portduino_config.rf95_max_power
#endif #endif
#ifndef RF95_MAX_POWER #ifndef RF95_MAX_POWER
#define RF95_MAX_POWER 20 #define RF95_MAX_POWER 20
@@ -94,16 +94,16 @@ void RF95Interface::setTransmitEnable(bool txon)
#ifdef RF95_TXEN #ifdef RF95_TXEN
digitalWrite(RF95_TXEN, txon ? 1 : 0); digitalWrite(RF95_TXEN, txon ? 1 : 0);
#elif ARCH_PORTDUINO #elif ARCH_PORTDUINO
if (settingsMap[txen_pin] != RADIOLIB_NC) { if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
digitalWrite(settingsMap[txen_pin], txon ? 1 : 0); digitalWrite(portduino_config.lora_txen_pin.pin, txon ? 1 : 0);
} }
#endif #endif
#ifdef RF95_RXEN #ifdef RF95_RXEN
digitalWrite(RF95_RXEN, txon ? 0 : 1); digitalWrite(RF95_RXEN, txon ? 0 : 1);
#elif ARCH_PORTDUINO #elif ARCH_PORTDUINO
if (settingsMap[rxen_pin] != RADIOLIB_NC) { if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen_pin], txon ? 0 : 1); digitalWrite(portduino_config.lora_rxen_pin.pin, txon ? 0 : 1);
} }
#endif #endif
} }
@@ -164,13 +164,13 @@ bool RF95Interface::init()
digitalWrite(RF95_RXEN, 1); digitalWrite(RF95_RXEN, 1);
#endif #endif
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (settingsMap[txen_pin] != RADIOLIB_NC) { if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
pinMode(settingsMap[txen_pin], OUTPUT); pinMode(portduino_config.lora_txen_pin.pin, OUTPUT);
digitalWrite(settingsMap[txen_pin], 0); digitalWrite(portduino_config.lora_txen_pin.pin, 0);
} }
if (settingsMap[rxen_pin] != RADIOLIB_NC) { if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
pinMode(settingsMap[rxen_pin], OUTPUT); pinMode(portduino_config.lora_rxen_pin.pin, OUTPUT);
digitalWrite(settingsMap[rxen_pin], 0); digitalWrite(portduino_config.lora_rxen_pin.pin, 0);
} }
#endif #endif
setTransmitEnable(false); setTransmitEnable(false);
+1 -1
View File
@@ -417,7 +417,7 @@ void RadioLibInterface::handleReceiveInterrupt()
int state = iface->readData((uint8_t *)&radioBuffer, length); int state = iface->readData((uint8_t *)&radioBuffer, length);
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (settingsMap[logoutputlevel] == level_trace) { if (portduino_config.logoutputlevel == level_trace) {
printBytes("Raw incoming packet: ", (uint8_t *)&radioBuffer, length); printBytes("Raw incoming packet: ", (uint8_t *)&radioBuffer, length);
} }
#endif #endif
+4 -1
View File
@@ -58,7 +58,10 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
// marked as wantAck // marked as wantAck
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, old->packet->channel); sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, old->packet->channel);
stopRetransmission(key); // Only stop retransmissions if the rebroadcast came via LoRa
if (p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA) {
stopRetransmission(key);
}
} else { } else {
LOG_DEBUG("Didn't find pending packet"); LOG_DEBUG("Didn't find pending packet");
} }
+2 -2
View File
@@ -446,7 +446,7 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
#if ENABLE_JSON_LOGGING #if ENABLE_JSON_LOGGING
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str()); LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
#elif ARCH_PORTDUINO #elif ARCH_PORTDUINO
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) {
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str()); LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
} }
#endif #endif
@@ -685,7 +685,7 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
LOG_TRACE("%s", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str()); LOG_TRACE("%s", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str());
#elif ARCH_PORTDUINO #elif ARCH_PORTDUINO
// Even ignored packets get logged in the trace // Even ignored packets get logged in the trace
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) { if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) {
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
LOG_TRACE("%s", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str()); LOG_TRACE("%s", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str());
} }
+3 -3
View File
@@ -1,13 +1,13 @@
#include "STM32WLE5JCInterface.h"
#include "configuration.h" #include "configuration.h"
#ifdef ARCH_STM32WL
#include "STM32WLE5JCInterface.h"
#include "error.h" #include "error.h"
#ifndef STM32WLx_MAX_POWER #ifndef STM32WLx_MAX_POWER
#define STM32WLx_MAX_POWER 22 #define STM32WLx_MAX_POWER 22
#endif #endif
#ifdef ARCH_STM32WL
STM32WLE5JCInterface::STM32WLE5JCInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, STM32WLE5JCInterface::STM32WLE5JCInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq,
RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy)
: SX126xInterface(hal, cs, irq, rst, busy) : SX126xInterface(hal, cs, irq, rst, busy)
+2 -11
View File
@@ -1,8 +1,8 @@
#pragma once #pragma once
#include "SX126xInterface.h"
#ifdef ARCH_STM32WL #ifdef ARCH_STM32WL
#include "SX126xInterface.h"
#include "rfswitch.h"
/** /**
* Our adapter for STM32WLE5JC radios * Our adapter for STM32WLE5JC radios
@@ -16,13 +16,4 @@ class STM32WLE5JCInterface : public SX126xInterface<STM32WLx>
virtual bool init() override; virtual bool init() override;
}; };
/* https://wiki.seeedstudio.com/LoRa-E5_STM32WLE5JC_Module/
* Wio-E5 module ONLY transmits through RFO_HP
* Receive: PA4=1, PA5=0
* Transmit(high output power, SMPS mode): PA4=0, PA5=1 */
static const RADIOLIB_PIN_TYPE rfswitch_pins[5] = {PA4, PA5, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
static const Module::RfSwitchMode_t rfswitch_table[4] = {
{STM32WLx::MODE_IDLE, {LOW, LOW}}, {STM32WLx::MODE_RX, {HIGH, LOW}}, {STM32WLx::MODE_TX_HP, {LOW, HIGH}}, END_OF_MODE_TABLE};
#endif // ARCH_STM32WL #endif // ARCH_STM32WL
+9 -9
View File
@@ -12,7 +12,7 @@
// Particular boards might define a different max power based on what their hardware can do, default to max power output if not // Particular boards might define a different max power based on what their hardware can do, default to max power output if not
// specified (may be dangerous if using external PA and SX126x power config forgotten) // specified (may be dangerous if using external PA and SX126x power config forgotten)
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
#define SX126X_MAX_POWER settingsMap[sx126x_max_power] #define SX126X_MAX_POWER portduino_config.sx126x_max_power
#endif #endif
#ifndef SX126X_MAX_POWER #ifndef SX126X_MAX_POWER
#define SX126X_MAX_POWER 22 #define SX126X_MAX_POWER 22
@@ -53,10 +53,10 @@ template <typename T> bool SX126xInterface<T>::init()
#endif #endif
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000; tcxoVoltage = (float)portduino_config.dio3_tcxo_voltage / 1000;
if (settingsMap[sx126x_ant_sw_pin] != RADIOLIB_NC) { if (portduino_config.lora_sx126x_ant_sw_pin.pin != RADIOLIB_NC) {
digitalWrite(settingsMap[sx126x_ant_sw_pin], HIGH); digitalWrite(portduino_config.lora_sx126x_ant_sw_pin.pin, HIGH);
pinMode(settingsMap[sx126x_ant_sw_pin], OUTPUT); pinMode(portduino_config.lora_sx126x_ant_sw_pin.pin, OUTPUT);
} }
#endif #endif
if (tcxoVoltage == 0.0) if (tcxoVoltage == 0.0)
@@ -98,7 +98,7 @@ template <typename T> bool SX126xInterface<T>::init()
bool dio2AsRfSwitch = true; bool dio2AsRfSwitch = true;
#elif defined(ARCH_PORTDUINO) #elif defined(ARCH_PORTDUINO)
bool dio2AsRfSwitch = false; bool dio2AsRfSwitch = false;
if (settingsMap[dio2_as_rf_switch]) { if (portduino_config.dio2_as_rf_switch) {
dio2AsRfSwitch = true; dio2AsRfSwitch = true;
} }
#else #else
@@ -112,9 +112,9 @@ template <typename T> bool SX126xInterface<T>::init()
// no effect // no effect
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (res == RADIOLIB_ERR_NONE) { if (res == RADIOLIB_ERR_NONE) {
LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen_pin], LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", portduino_config.lora_rxen_pin.pin,
settingsMap[txen_pin]); portduino_config.lora_txen_pin.pin);
lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]); lora.setRfSwitchPins(portduino_config.lora_rxen_pin.pin, portduino_config.lora_txen_pin.pin);
} }
#else #else
#ifndef SX126X_RXEN #ifndef SX126X_RXEN
+22 -21
View File
@@ -11,7 +11,7 @@
// Particular boards might define a different max power based on what their hardware can do // Particular boards might define a different max power based on what their hardware can do
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
#define SX128X_MAX_POWER settingsMap[sx128x_max_power] #define SX128X_MAX_POWER portduino_config.sx128x_max_power
#endif #endif
#ifndef SX128X_MAX_POWER #ifndef SX128X_MAX_POWER
#define SX128X_MAX_POWER 13 #define SX128X_MAX_POWER 13
@@ -41,13 +41,13 @@ template <typename T> bool SX128xInterface<T>::init()
#endif #endif
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (settingsMap[rxen_pin] != RADIOLIB_NC) { if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
pinMode(settingsMap[rxen_pin], OUTPUT); pinMode(portduino_config.lora_rxen_pin.pin, OUTPUT);
digitalWrite(settingsMap[rxen_pin], LOW); // Set low before becoming an output digitalWrite(portduino_config.lora_rxen_pin.pin, LOW); // Set low before becoming an output
} }
if (settingsMap[txen_pin] != RADIOLIB_NC) { if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
pinMode(settingsMap[txen_pin], OUTPUT); pinMode(portduino_config.lora_txen_pin.pin, OUTPUT);
digitalWrite(settingsMap[txen_pin], LOW); // Set low before becoming an output digitalWrite(portduino_config.lora_txen_pin.pin, LOW); // Set low before becoming an output
} }
#else #else
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode #if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode
@@ -93,8 +93,9 @@ template <typename T> bool SX128xInterface<T>::init()
lora.setRfSwitchPins(SX128X_RXEN, SX128X_TXEN); lora.setRfSwitchPins(SX128X_RXEN, SX128X_TXEN);
} }
#elif ARCH_PORTDUINO #elif ARCH_PORTDUINO
if (res == RADIOLIB_ERR_NONE && settingsMap[rxen_pin] != RADIOLIB_NC && settingsMap[txen_pin] != RADIOLIB_NC) { if (res == RADIOLIB_ERR_NONE && portduino_config.lora_rxen_pin.pin != RADIOLIB_NC &&
lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]); portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
lora.setRfSwitchPins(portduino_config.lora_rxen_pin.pin, portduino_config.lora_txen_pin.pin);
} }
#endif #endif
@@ -174,11 +175,11 @@ template <typename T> void SX128xInterface<T>::setStandby()
LOG_ERROR("SX128x standby %s%d", radioLibErr, err); LOG_ERROR("SX128x standby %s%d", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE); assert(err == RADIOLIB_ERR_NONE);
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (settingsMap[rxen_pin] != RADIOLIB_NC) { if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen_pin], LOW); digitalWrite(portduino_config.lora_rxen_pin.pin, LOW);
} }
if (settingsMap[txen_pin] != RADIOLIB_NC) { if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
digitalWrite(settingsMap[txen_pin], LOW); digitalWrite(portduino_config.lora_txen_pin.pin, LOW);
} }
#else #else
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn off RX and TX power #if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn off RX and TX power
@@ -210,11 +211,11 @@ template <typename T> void SX128xInterface<T>::addReceiveMetadata(meshtastic_Mes
template <typename T> void SX128xInterface<T>::configHardwareForSend() template <typename T> void SX128xInterface<T>::configHardwareForSend()
{ {
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (settingsMap[txen_pin] != RADIOLIB_NC) { if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
digitalWrite(settingsMap[txen_pin], HIGH); digitalWrite(portduino_config.lora_txen_pin.pin, HIGH);
} }
if (settingsMap[rxen_pin] != RADIOLIB_NC) { if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen_pin], LOW); digitalWrite(portduino_config.lora_rxen_pin.pin, LOW);
} }
#else #else
@@ -241,11 +242,11 @@ template <typename T> void SX128xInterface<T>::startReceive()
setStandby(); setStandby();
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (settingsMap[rxen_pin] != RADIOLIB_NC) { if (portduino_config.lora_rxen_pin.pin != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen_pin], HIGH); digitalWrite(portduino_config.lora_rxen_pin.pin, HIGH);
} }
if (settingsMap[txen_pin] != RADIOLIB_NC) { if (portduino_config.lora_txen_pin.pin != RADIOLIB_NC) {
digitalWrite(settingsMap[txen_pin], LOW); digitalWrite(portduino_config.lora_txen_pin.pin, LOW);
} }
#else #else
+8 -3
View File
@@ -64,7 +64,12 @@ typedef enum _meshtastic_Config_DeviceConfig_Role {
in areas not already covered by other routers, or to bridge around problematic terrain, in areas not already covered by other routers, or to bridge around problematic terrain,
but should not be given priority over other routers in order to avoid unnecessaraily but should not be given priority over other routers in order to avoid unnecessaraily
consuming hops. */ consuming hops. */
meshtastic_Config_DeviceConfig_Role_ROUTER_LATE = 11 meshtastic_Config_DeviceConfig_Role_ROUTER_LATE = 11,
/* Description: Treats packets from or to favorited nodes as ROUTER, and all other packets as CLIENT.
Technical Details: Used for stronger attic/roof nodes to distribute messages more widely
from weaker, indoor, or less-well-positioned nodes. Recommended for users with multiple nodes
where one CLIENT_BASE acts as a more powerful base station, such as an attic/roof node. */
meshtastic_Config_DeviceConfig_Role_CLIENT_BASE = 12
} meshtastic_Config_DeviceConfig_Role; } meshtastic_Config_DeviceConfig_Role;
/* Defines the device's behavior for how messages are rebroadcast */ /* Defines the device's behavior for how messages are rebroadcast */
@@ -646,8 +651,8 @@ extern "C" {
/* Helper constants for enums */ /* Helper constants for enums */
#define _meshtastic_Config_DeviceConfig_Role_MIN meshtastic_Config_DeviceConfig_Role_CLIENT #define _meshtastic_Config_DeviceConfig_Role_MIN meshtastic_Config_DeviceConfig_Role_CLIENT
#define _meshtastic_Config_DeviceConfig_Role_MAX meshtastic_Config_DeviceConfig_Role_ROUTER_LATE #define _meshtastic_Config_DeviceConfig_Role_MAX meshtastic_Config_DeviceConfig_Role_CLIENT_BASE
#define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_ROUTER_LATE+1)) #define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_CLIENT_BASE+1))
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN meshtastic_Config_DeviceConfig_RebroadcastMode_ALL #define _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN meshtastic_Config_DeviceConfig_RebroadcastMode_ALL
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY #define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY
+4
View File
@@ -270,6 +270,10 @@ typedef enum _meshtastic_HardwareModel {
/* MeshSolar is an integrated power management and communication solution designed for outdoor low-power devices. /* MeshSolar is an integrated power management and communication solution designed for outdoor low-power devices.
https://heltec.org/project/meshsolar/ */ https://heltec.org/project/meshsolar/ */
meshtastic_HardwareModel_HELTEC_MESH_SOLAR = 108, meshtastic_HardwareModel_HELTEC_MESH_SOLAR = 108,
/* Lilygo T-Echo Lite */
meshtastic_HardwareModel_T_ECHO_LITE = 109,
/* New Heltec LoRA32 with ESP32-S3 CPU */
meshtastic_HardwareModel_HELTEC_V4 = 110,
/* ------------------------------------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */ ------------------------------------------------------------------------------------------------------------------------------------------ */
+35 -15
View File
@@ -342,6 +342,11 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
res->print(value->Stringify().c_str()); res->print(value->Stringify().c_str());
delete value; delete value;
// Clean up the fileList to prevent memory leak
for (auto *val : fileList) {
delete val;
}
} }
void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res) void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
@@ -610,33 +615,38 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->println("<pre>"); res->println("<pre>");
} }
// Helper lambda to create JSON array and clean up memory properly
auto createJSONArrayFromLog = [](uint32_t *logArray, int count) -> JSONValue * {
JSONArray tempArray;
for (int i = 0; i < count; i++) {
tempArray.push_back(new JSONValue((int)logArray[i]));
}
JSONValue *result = new JSONValue(tempArray);
// Clean up original array to prevent memory leak
for (auto *val : tempArray) {
delete val;
}
return result;
};
// data->airtime->tx_log // data->airtime->tx_log
JSONArray txLogValues;
uint32_t *logArray; uint32_t *logArray;
logArray = airTime->airtimeReport(TX_LOG); logArray = airTime->airtimeReport(TX_LOG);
for (int i = 0; i < airTime->getPeriodsToLog(); i++) { JSONValue *txLogJsonValue = createJSONArrayFromLog(logArray, airTime->getPeriodsToLog());
txLogValues.push_back(new JSONValue((int)logArray[i]));
}
// data->airtime->rx_log // data->airtime->rx_log
JSONArray rxLogValues;
logArray = airTime->airtimeReport(RX_LOG); logArray = airTime->airtimeReport(RX_LOG);
for (int i = 0; i < airTime->getPeriodsToLog(); i++) { JSONValue *rxLogJsonValue = createJSONArrayFromLog(logArray, airTime->getPeriodsToLog());
rxLogValues.push_back(new JSONValue((int)logArray[i]));
}
// data->airtime->rx_all_log // data->airtime->rx_all_log
JSONArray rxAllLogValues;
logArray = airTime->airtimeReport(RX_ALL_LOG); logArray = airTime->airtimeReport(RX_ALL_LOG);
for (int i = 0; i < airTime->getPeriodsToLog(); i++) { JSONValue *rxAllLogJsonValue = createJSONArrayFromLog(logArray, airTime->getPeriodsToLog());
rxAllLogValues.push_back(new JSONValue((int)logArray[i]));
}
// data->airtime // data->airtime
JSONObject jsonObjAirtime; JSONObject jsonObjAirtime;
jsonObjAirtime["tx_log"] = new JSONValue(txLogValues); jsonObjAirtime["tx_log"] = txLogJsonValue;
jsonObjAirtime["rx_log"] = new JSONValue(rxLogValues); jsonObjAirtime["rx_log"] = rxLogJsonValue;
jsonObjAirtime["rx_all_log"] = new JSONValue(rxAllLogValues); jsonObjAirtime["rx_all_log"] = rxAllLogJsonValue;
jsonObjAirtime["channel_utilization"] = new JSONValue(airTime->channelUtilizationPercent()); jsonObjAirtime["channel_utilization"] = new JSONValue(airTime->channelUtilizationPercent());
jsonObjAirtime["utilization_tx"] = new JSONValue(airTime->utilizationTXPercent()); jsonObjAirtime["utilization_tx"] = new JSONValue(airTime->utilizationTXPercent());
jsonObjAirtime["seconds_since_boot"] = new JSONValue(int(airTime->getSecondsSinceBoot())); jsonObjAirtime["seconds_since_boot"] = new JSONValue(int(airTime->getSecondsSinceBoot()));
@@ -765,6 +775,11 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res)
JSONValue *value = new JSONValue(jsonObjOuter); JSONValue *value = new JSONValue(jsonObjOuter);
res->print(value->Stringify().c_str()); res->print(value->Stringify().c_str());
delete value; delete value;
// Clean up the nodesArray to prevent memory leak
for (auto *val : nodesArray) {
delete val;
}
} }
/* /*
@@ -955,5 +970,10 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
JSONValue *value = new JSONValue(jsonObjOuter); JSONValue *value = new JSONValue(jsonObjOuter);
res->print(value->Stringify().c_str()); res->print(value->Stringify().c_str());
delete value; delete value;
// Clean up the networkObjs to prevent memory leak
for (auto *val : networkObjs) {
delete val;
}
} }
#endif #endif
+5 -5
View File
@@ -65,8 +65,8 @@ mail: marchammermann@googlemail.com
#define DEFAULT_REALM "default_realm" #define DEFAULT_REALM "default_realm"
#define PREFIX "" #define PREFIX ""
#define KEY_PATH settingsStrings[websslkeypath].c_str() #define KEY_PATH portduino_config.webserver_ssl_key_path.c_str()
#define CERT_PATH settingsStrings[websslcertpath].c_str() #define CERT_PATH portduino_config.webserver_ssl_cert_path.c_str()
struct _file_config configWeb; struct _file_config configWeb;
@@ -458,8 +458,8 @@ PiWebServerThread::PiWebServerThread()
} }
} }
if (settingsMap[webserverport] != 0) { if (portduino_config.webserverport != 0) {
webservport = settingsMap[webserverport]; webservport = portduino_config.webserverport;
LOG_INFO("Use webserver port from yaml config %i ", webservport); LOG_INFO("Use webserver port from yaml config %i ", webservport);
} else { } else {
LOG_INFO("Webserver port in yaml config set to 0, defaulting to port 9443"); LOG_INFO("Webserver port in yaml config set to 0, defaulting to port 9443");
@@ -490,7 +490,7 @@ PiWebServerThread::PiWebServerThread()
u_map_put(&configWeb.mime_types, ".ico", "image/x-icon"); u_map_put(&configWeb.mime_types, ".ico", "image/x-icon");
u_map_put(&configWeb.mime_types, ".svg", "image/svg+xml"); u_map_put(&configWeb.mime_types, ".svg", "image/svg+xml");
webrootpath = settingsStrings[webserverrootpath]; webrootpath = portduino_config.webserver_root_path;
configWeb.files_path = (char *)webrootpath.c_str(); configWeb.files_path = (char *)webrootpath.c_str();
configWeb.url_prefix = ""; configWeb.url_prefix = "";
+29
View File
@@ -1040,19 +1040,32 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
res.get_module_config_response.payload_variant.serial = moduleConfig.serial; res.get_module_config_response.payload_variant.serial = moduleConfig.serial;
break; break;
case meshtastic_AdminMessage_ModuleConfigType_EXTNOTIF_CONFIG: case meshtastic_AdminMessage_ModuleConfigType_EXTNOTIF_CONFIG:
#if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION
LOG_INFO("Get module config: External Notification"); LOG_INFO("Get module config: External Notification");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_external_notification_tag; res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_external_notification_tag;
res.get_module_config_response.payload_variant.external_notification = moduleConfig.external_notification; res.get_module_config_response.payload_variant.external_notification = moduleConfig.external_notification;
#else
LOG_DEBUG("External Notification module excluded from build, skipping config");
#endif
break; break;
case meshtastic_AdminMessage_ModuleConfigType_STOREFORWARD_CONFIG: case meshtastic_AdminMessage_ModuleConfigType_STOREFORWARD_CONFIG:
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
LOG_INFO("Get module config: Store & Forward"); LOG_INFO("Get module config: Store & Forward");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_store_forward_tag; res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_store_forward_tag;
res.get_module_config_response.payload_variant.store_forward = moduleConfig.store_forward; res.get_module_config_response.payload_variant.store_forward = moduleConfig.store_forward;
#else
LOG_DEBUG("Store & Forward module excluded from build, skipping config");
#endif
break; break;
case meshtastic_AdminMessage_ModuleConfigType_RANGETEST_CONFIG: case meshtastic_AdminMessage_ModuleConfigType_RANGETEST_CONFIG:
#if !MESHTASTIC_EXCLUDE_RANGETEST
LOG_INFO("Get module config: Range Test"); LOG_INFO("Get module config: Range Test");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_range_test_tag; res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_range_test_tag;
res.get_module_config_response.payload_variant.range_test = moduleConfig.range_test; res.get_module_config_response.payload_variant.range_test = moduleConfig.range_test;
#else
LOG_DEBUG("Range Test module excluded from build, skipping config");
// Don't set payload variant - will result in empty response
#endif
break; break;
case meshtastic_AdminMessage_ModuleConfigType_TELEMETRY_CONFIG: case meshtastic_AdminMessage_ModuleConfigType_TELEMETRY_CONFIG:
LOG_INFO("Get module config: Telemetry"); LOG_INFO("Get module config: Telemetry");
@@ -1065,9 +1078,13 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
res.get_module_config_response.payload_variant.canned_message = moduleConfig.canned_message; res.get_module_config_response.payload_variant.canned_message = moduleConfig.canned_message;
break; break;
case meshtastic_AdminMessage_ModuleConfigType_AUDIO_CONFIG: case meshtastic_AdminMessage_ModuleConfigType_AUDIO_CONFIG:
#if !MESHTASTIC_EXCLUDE_AUDIO
LOG_INFO("Get module config: Audio"); LOG_INFO("Get module config: Audio");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_audio_tag; res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_audio_tag;
res.get_module_config_response.payload_variant.audio = moduleConfig.audio; res.get_module_config_response.payload_variant.audio = moduleConfig.audio;
#else
LOG_DEBUG("Audio module excluded from build, skipping config");
#endif
break; break;
case meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG: case meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG:
LOG_INFO("Get module config: Remote Hardware"); LOG_INFO("Get module config: Remote Hardware");
@@ -1080,19 +1097,31 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
res.get_module_config_response.payload_variant.neighbor_info = moduleConfig.neighbor_info; res.get_module_config_response.payload_variant.neighbor_info = moduleConfig.neighbor_info;
break; break;
case meshtastic_AdminMessage_ModuleConfigType_DETECTIONSENSOR_CONFIG: case meshtastic_AdminMessage_ModuleConfigType_DETECTIONSENSOR_CONFIG:
#if !(NO_EXT_GPIO || MESHTASTIC_EXCLUDE_DETECTIONSENSOR)
LOG_INFO("Get module config: Detection Sensor"); LOG_INFO("Get module config: Detection Sensor");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_detection_sensor_tag; res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_detection_sensor_tag;
res.get_module_config_response.payload_variant.detection_sensor = moduleConfig.detection_sensor; res.get_module_config_response.payload_variant.detection_sensor = moduleConfig.detection_sensor;
#else
LOG_DEBUG("Detection Sensor module excluded from build, skipping config");
#endif
break; break;
case meshtastic_AdminMessage_ModuleConfigType_AMBIENTLIGHTING_CONFIG: case meshtastic_AdminMessage_ModuleConfigType_AMBIENTLIGHTING_CONFIG:
#if !MESHTASTIC_EXCLUDE_AMBIENTLIGHTING
LOG_INFO("Get module config: Ambient Lighting"); LOG_INFO("Get module config: Ambient Lighting");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_ambient_lighting_tag; res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_ambient_lighting_tag;
res.get_module_config_response.payload_variant.ambient_lighting = moduleConfig.ambient_lighting; res.get_module_config_response.payload_variant.ambient_lighting = moduleConfig.ambient_lighting;
#else
LOG_DEBUG("Ambient Lighting module excluded from build, skipping config");
#endif
break; break;
case meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG: case meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG:
#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
LOG_INFO("Get module config: Paxcounter"); LOG_INFO("Get module config: Paxcounter");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_paxcounter_tag; res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_paxcounter_tag;
res.get_module_config_response.payload_variant.paxcounter = moduleConfig.paxcounter; res.get_module_config_response.payload_variant.paxcounter = moduleConfig.paxcounter;
#else
LOG_DEBUG("Paxcounter module excluded from build, skipping config");
#endif
break; break;
} }
+15 -1
View File
@@ -30,7 +30,8 @@
namespace graphics namespace graphics
{ {
extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool battery_only); extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool force_no_invert,
bool show_date);
} }
#if __has_include(<Adafruit_AHTX0.h>) #if __has_include(<Adafruit_AHTX0.h>)
#include "Sensor/AHT10.h" #include "Sensor/AHT10.h"
@@ -198,6 +199,13 @@ T1000xSensor t1000xSensor;
IndicatorSensor indicatorSensor; IndicatorSensor indicatorSensor;
#endif #endif
#if __has_include(<Adafruit_TSL2561_U.h>)
#include "Sensor/TSL2561Sensor.h"
TSL2561Sensor tsl2561Sensor;
#else
NullSensor tsl2561Sensor;
#endif
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10 #define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true #define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
@@ -296,6 +304,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = max17048Sensor.runOnce(); result = max17048Sensor.runOnce();
if (cgRadSens.hasSensor()) if (cgRadSens.hasSensor())
result = cgRadSens.runOnce(); result = cgRadSens.runOnce();
if (tsl2561Sensor.hasSensor())
result = tsl2561Sensor.runOnce();
if (pct2075Sensor.hasSensor()) if (pct2075Sensor.hasSensor())
result = pct2075Sensor.runOnce(); result = pct2075Sensor.runOnce();
// this only works on the wismesh hub with the solar option. This is not an I2C sensor, so we don't need the // this only works on the wismesh hub with the solar option. This is not an I2C sensor, so we don't need the
@@ -642,6 +652,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
valid = valid && nau7802Sensor.getMetrics(m); valid = valid && nau7802Sensor.getMetrics(m);
hasSensor = true; hasSensor = true;
} }
if (tsl2561Sensor.hasSensor()) {
valid = valid && tsl2561Sensor.getMetrics(m);
hasSensor = true;
}
if (aht10Sensor.hasSensor()) { if (aht10Sensor.hasSensor()) {
if (!bmp280Sensor.hasSensor() && !bmp3xxSensor.hasSensor()) { if (!bmp280Sensor.hasSensor() && !bmp3xxSensor.hasSensor()) {
valid = valid && aht10Sensor.getMetrics(m); valid = valid && aht10Sensor.getMetrics(m);
+5 -5
View File
@@ -9,11 +9,11 @@
int32_t HostMetricsModule::runOnce() int32_t HostMetricsModule::runOnce()
{ {
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
if (settingsMap[hostMetrics_interval] == 0) { if (portduino_config.hostMetrics_interval == 0) {
return disable(); return disable();
} else { } else {
sendMetrics(); sendMetrics();
return 60 * 1000 * settingsMap[hostMetrics_interval]; return 60 * 1000 * portduino_config.hostMetrics_interval;
} }
#else #else
return disable(); return disable();
@@ -110,8 +110,8 @@ meshtastic_Telemetry HostMetricsModule::getHostMetrics()
proc_loadavg.close(); proc_loadavg.close();
} }
} }
if (settingsStrings[hostMetrics_user_command] != "") { if (portduino_config.hostMetrics_user_command != "") {
std::string userCommandResult = exec(settingsStrings[hostMetrics_user_command].c_str()); std::string userCommandResult = exec(portduino_config.hostMetrics_user_command.c_str());
if (userCommandResult.length() > 1) { if (userCommandResult.length() > 1) {
strncpy(t.variant.host_metrics.user_string, userCommandResult.c_str(), sizeof(t.variant.host_metrics.user_string)); strncpy(t.variant.host_metrics.user_string, userCommandResult.c_str(), sizeof(t.variant.host_metrics.user_string));
t.variant.host_metrics.user_string[sizeof(t.variant.host_metrics.user_string) - 1] = '\0'; t.variant.host_metrics.user_string[sizeof(t.variant.host_metrics.user_string) - 1] = '\0';
@@ -135,7 +135,7 @@ bool HostMetricsModule::sendMetrics()
p->to = NODENUM_BROADCAST; p->to = NODENUM_BROADCAST;
p->decoded.want_response = false; p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
p->channel = settingsMap[hostMetrics_channel]; p->channel = portduino_config.hostMetrics_channel;
LOG_INFO("Send packet to mesh"); LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true); service->sendToMesh(p, RX_SRC_LOCAL, true);
return true; return true;
+2 -1
View File
@@ -24,7 +24,8 @@
namespace graphics namespace graphics
{ {
extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool battery_only); extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool force_no_invert,
bool show_date);
} }
int32_t PowerTelemetryModule::runOnce() int32_t PowerTelemetryModule::runOnce()
@@ -0,0 +1,41 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<Adafruit_TSL2561_U.h>)
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TSL2561Sensor.h"
#include "TelemetrySensor.h"
#include <Adafruit_TSL2561_U.h>
TSL2561Sensor::TSL2561Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_TSL2561, "TSL2561") {}
int32_t TSL2561Sensor::runOnce()
{
LOG_INFO("Init sensor: %s", sensorName);
if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
status = tsl.begin(nodeTelemetrySensorsMap[sensorType].second);
return initI2CSensor();
}
void TSL2561Sensor::setup()
{
tsl.setGain(TSL2561_GAIN_1X);
tsl.setIntegrationTime(TSL2561_INTEGRATIONTIME_101MS);
}
bool TSL2561Sensor::getMetrics(meshtastic_Telemetry *measurement)
{
measurement->variant.environment_metrics.has_lux = true;
sensors_event_t event;
tsl.getEvent(&event);
measurement->variant.environment_metrics.lux = event.light;
LOG_INFO("Lux: %f", measurement->variant.environment_metrics.lux);
return true;
}
#endif
@@ -0,0 +1,23 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<Adafruit_TSL2561_U.h>)
#include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <Adafruit_TSL2561_U.h>
class TSL2561Sensor : public TelemetrySensor
{
private:
// The magic number is a sensor id, the actual value doesn't matter
Adafruit_TSL2561_Unified tsl = Adafruit_TSL2561_Unified(TSL2561_ADDR_LOW, 12345);
protected:
virtual void setup() override;
public:
TSL2561Sensor();
virtual int32_t runOnce() override;
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
};
#endif
+1 -1
View File
@@ -7,7 +7,7 @@
#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C #if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C
#if defined(RAK_4631) && !defined(RAK2560) && __has_include(<Rak_BMX160.h>) #if !defined(RAK2560) && __has_include(<Rak_BMX160.h>)
#include "Fusion/Fusion.h" #include "Fusion/Fusion.h"
#include <Rak_BMX160.h> #include <Rak_BMX160.h>
+214 -320
View File
@@ -9,7 +9,6 @@
#include "api/ServerAPI.h" #include "api/ServerAPI.h"
#include "linux/gpio/LinuxGPIOPin.h" #include "linux/gpio/LinuxGPIOPin.h"
#include "meshUtils.h" #include "meshUtils.h"
#include "yaml-cpp/yaml.h"
#include <ErriezCRC32.h> #include <ErriezCRC32.h>
#include <Utility.h> #include <Utility.h>
#include <assert.h> #include <assert.h>
@@ -28,14 +27,13 @@
#include "platform/portduino/USBHal.h" #include "platform/portduino/USBHal.h"
std::map<configNames, int> settingsMap;
std::map<configNames, std::string> settingsStrings;
portduino_config_struct portduino_config; portduino_config_struct portduino_config;
std::ofstream traceFile; std::ofstream traceFile;
Ch341Hal *ch341Hal = nullptr; Ch341Hal *ch341Hal = nullptr;
char *configPath = nullptr; char *configPath = nullptr;
char *optionMac = nullptr; char *optionMac = nullptr;
bool verboseEnabled = false; bool verboseEnabled = false;
bool yamlOnly = false;
const char *argp_program_version = optstr(APP_VERSION); const char *argp_program_version = optstr(APP_VERSION);
@@ -75,6 +73,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
case 'v': case 'v':
verboseEnabled = true; verboseEnabled = true;
break; break;
case 'y':
yamlOnly = true;
break;
case ARGP_KEY_ARG: case ARGP_KEY_ARG:
return 0; return 0;
default: default:
@@ -90,6 +91,7 @@ void portduinoCustomInit()
{"hwid", 'h', "HWID", 0, "The mac address to assign to this virtual machine"}, {"hwid", 'h', "HWID", 0, "The mac address to assign to this virtual machine"},
{"sim", 's', 0, 0, "Run in Simulated radio mode"}, {"sim", 's', 0, 0, "Run in Simulated radio mode"},
{"verbose", 'v', 0, 0, "Set log level to full debug"}, {"verbose", 'v', 0, 0, "Set log level to full debug"},
{"output-yaml", 'y', 0, 0, "Output config yaml and exit"},
{0}}; {0}};
static void *childArguments; static void *childArguments;
static char doc[] = "Meshtastic native build."; static char doc[] = "Meshtastic native build.";
@@ -115,8 +117,8 @@ void getMacAddr(uint8_t *dmac)
dmac[4] = hwId >> 8; dmac[4] = hwId >> 8;
dmac[5] = hwId & 0xff; dmac[5] = hwId & 0xff;
} }
} else if (settingsStrings[mac_address].length() > 11) { } else if (portduino_config.mac_address.length() > 11) {
MAC_from_string(settingsStrings[mac_address], dmac); MAC_from_string(portduino_config.mac_address, dmac);
exit; exit;
} else { } else {
@@ -148,89 +150,46 @@ void getMacAddr(uint8_t *dmac)
*/ */
void portduinoSetup() void portduinoSetup()
{ {
printf("Set up Meshtastic on Portduino...\n");
int max_GPIO = 0; int max_GPIO = 0;
const configNames GPIO_lines[] = {cs_pin,
irq_pin,
busy_pin,
reset_pin,
sx126x_ant_sw_pin,
txen_pin,
rxen_pin,
displayDC,
displayCS,
displayBacklight,
displayBacklightPWMChannel,
displayReset,
touchscreenCS,
touchscreenIRQ,
userButtonPin,
tbUpPin,
tbDownPin,
tbLeftPin,
tbRightPin,
tbPressPin};
std::string gpioChipName = "gpiochip"; std::string gpioChipName = "gpiochip";
settingsStrings[i2cdev] = ""; portduino_config.displayPanel = no_screen;
settingsStrings[keyboardDevice] = "";
settingsStrings[pointerDevice] = "";
settingsStrings[webserverrootpath] = "";
settingsStrings[spidev] = "";
settingsStrings[displayspidev] = "";
settingsMap[spiSpeed] = 2000000;
settingsMap[ascii_logs] = !isatty(1);
settingsMap[displayPanel] = no_screen;
settingsMap[touchscreenModule] = no_touchscreen;
settingsMap[tbUpPin] = RADIOLIB_NC;
settingsMap[tbDownPin] = RADIOLIB_NC;
settingsMap[tbLeftPin] = RADIOLIB_NC;
settingsMap[tbRightPin] = RADIOLIB_NC;
settingsMap[tbPressPin] = RADIOLIB_NC;
YAML::Node yamlConfig;
if (portduino_config.force_simradio == true) { if (portduino_config.force_simradio == true) {
settingsMap[use_simradio] = true; portduino_config.lora_module = use_simradio;
} else if (configPath != nullptr) { } else if (configPath != nullptr) {
if (loadConfig(configPath)) { if (loadConfig(configPath)) {
std::cout << "Using " << configPath << " as config file" << std::endl; if (!yamlOnly)
std::cout << "Using " << configPath << " as config file" << std::endl;
} else { } else {
std::cout << "Unable to use " << configPath << " as config file" << std::endl; std::cout << "Unable to use " << configPath << " as config file" << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} else if (access("config.yaml", R_OK) == 0) { } else if (access("config.yaml", R_OK) == 0) {
if (loadConfig("config.yaml")) { if (loadConfig("config.yaml")) {
std::cout << "Using local config.yaml as config file" << std::endl; if (!yamlOnly)
std::cout << "Using local config.yaml as config file" << std::endl;
} else { } else {
std::cout << "Unable to use local config.yaml as config file" << std::endl; std::cout << "Unable to use local config.yaml as config file" << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} else if (access("/etc/meshtasticd/config.yaml", R_OK) == 0) { } else if (access("/etc/meshtasticd/config.yaml", R_OK) == 0) {
if (loadConfig("/etc/meshtasticd/config.yaml")) { if (loadConfig("/etc/meshtasticd/config.yaml")) {
std::cout << "Using /etc/meshtasticd/config.yaml as config file" << std::endl; if (!yamlOnly)
std::cout << "Using /etc/meshtasticd/config.yaml as config file" << std::endl;
} else { } else {
std::cout << "Unable to use /etc/meshtasticd/config.yaml as config file" << std::endl; std::cout << "Unable to use /etc/meshtasticd/config.yaml as config file" << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} else { } else {
std::cout << "No 'config.yaml' found..." << std::endl; if (!yamlOnly)
settingsMap[use_simradio] = true; std::cout << "No 'config.yaml' found..." << std::endl;
portduino_config.lora_module = use_simradio;
} }
if (settingsMap[use_simradio] == true) { if (portduino_config.config_directory != "") {
std::cout << "Running in simulated mode." << std::endl;
settingsMap[maxnodes] = 200; // Default to 200 nodes
settingsMap[logoutputlevel] = level_debug; // Default to debug
// Set the random seed equal to TCPPort to have a different seed per instance
randomSeed(TCPPort);
return;
}
if (settingsStrings[config_directory] != "") {
std::string filetype = ".yaml"; std::string filetype = ".yaml";
for (const std::filesystem::directory_entry &entry : for (const std::filesystem::directory_entry &entry :
std::filesystem::directory_iterator{settingsStrings[config_directory]}) { std::filesystem::directory_iterator{portduino_config.config_directory}) {
if (ends_with(entry.path().string(), ".yaml")) { if (ends_with(entry.path().string(), ".yaml")) {
std::cout << "Also using " << entry << " as additional config file" << std::endl; std::cout << "Also using " << entry << " as additional config file" << std::endl;
loadConfig(entry.path().c_str()); loadConfig(entry.path().c_str());
@@ -238,15 +197,28 @@ void portduinoSetup()
} }
} }
if (yamlOnly) {
std::cout << portduino_config.emit_yaml() << std::endl;
exit(EXIT_SUCCESS);
}
if (portduino_config.lora_module == use_simradio) {
std::cout << "Running in simulated mode." << std::endl;
portduino_config.MaxNodes = 200; // Default to 200 nodes
// Set the random seed equal to TCPPort to have a different seed per instance
randomSeed(TCPPort);
return;
}
// If LoRa `Module: auto` (default in config.yaml), // If LoRa `Module: auto` (default in config.yaml),
// attempt to auto config based on Product Strings // attempt to auto config based on Product Strings
if (settingsMap[use_autoconf] == true) { if (portduino_config.lora_module == use_autoconf) {
char autoconf_product[96] = {0}; char autoconf_product[96] = {0};
// Try CH341 // Try CH341
try { try {
std::cout << "autoconf: Looking for CH341 device..." << std::endl; std::cout << "autoconf: Looking for CH341 device..." << std::endl;
ch341Hal = ch341Hal = new Ch341Hal(0, portduino_config.lora_usb_serial_num, portduino_config.lora_usb_vid,
new Ch341Hal(0, settingsStrings[lora_usb_serial_num], settingsMap[lora_usb_vid], settingsMap[lora_usb_pid]); portduino_config.lora_usb_pid);
ch341Hal->getProductString(autoconf_product, 95); ch341Hal->getProductString(autoconf_product, 95);
delete ch341Hal; delete ch341Hal;
std::cout << "autoconf: Found CH341 device " << autoconf_product << std::endl; std::cout << "autoconf: Found CH341 device " << autoconf_product << std::endl;
@@ -323,7 +295,7 @@ void portduinoSetup()
if (mac_start != nullptr) { if (mac_start != nullptr) {
std::cout << "autoconf: Found mac data " << mac_start << std::endl; std::cout << "autoconf: Found mac data " << mac_start << std::endl;
if (strlen(mac_start) == 12) if (strlen(mac_start) == 12)
settingsStrings[mac_address] = std::string(mac_start); portduino_config.mac_address = std::string(mac_start);
} }
if (devID_start != nullptr) { if (devID_start != nullptr) {
std::cout << "autoconf: Found deviceid data " << devID_start << std::endl; std::cout << "autoconf: Found deviceid data " << devID_start << std::endl;
@@ -354,7 +326,7 @@ void portduinoSetup()
std::cerr << "autoconf: Unable to find config for " << autoconf_product << std::endl; std::cerr << "autoconf: Unable to find config for " << autoconf_product << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (loadConfig((settingsStrings[available_directory] + product_config).c_str())) { if (loadConfig((portduino_config.available_directory + product_config).c_str())) {
std::cout << "autoconf: Using " << product_config << " as config file for " << autoconf_product << std::endl; std::cout << "autoconf: Using " << product_config << " as config file for " << autoconf_product << std::endl;
} else { } else {
std::cerr << "autoconf: Unable to use " << product_config << " as config file for " << autoconf_product std::cerr << "autoconf: Unable to use " << product_config << " as config file for " << autoconf_product
@@ -363,15 +335,16 @@ void portduinoSetup()
} }
} else { } else {
std::cerr << "autoconf: Could not locate any devices" << std::endl; std::cerr << "autoconf: Could not locate any devices" << std::endl;
exit(EXIT_FAILURE);
} }
} }
// if we're using a usermode driver, we need to initialize it here, to get a serial number back for mac address // if we're using a usermode driver, we need to initialize it here, to get a serial number back for mac address
uint8_t dmac[6] = {0}; uint8_t dmac[6] = {0};
if (settingsStrings[spidev] == "ch341") { if (portduino_config.lora_spi_dev == "ch341") {
try { try {
ch341Hal = ch341Hal = new Ch341Hal(0, portduino_config.lora_usb_serial_num, portduino_config.lora_usb_vid,
new Ch341Hal(0, settingsStrings[lora_usb_serial_num], settingsMap[lora_usb_vid], settingsMap[lora_usb_pid]); portduino_config.lora_usb_pid);
} catch (std::exception &e) { } catch (std::exception &e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
std::cerr << "Could not initialize CH341 device!" << std::endl; std::cerr << "Could not initialize CH341 device!" << std::endl;
@@ -383,7 +356,7 @@ void portduinoSetup()
char product_string[96] = {0}; char product_string[96] = {0};
ch341Hal->getProductString(product_string, 95); ch341Hal->getProductString(product_string, 95);
std::cout << "CH341 Product " << product_string << std::endl; std::cout << "CH341 Product " << product_string << std::endl;
if (strlen(serial) == 8 && settingsStrings[mac_address].length() < 12) { if (strlen(serial) == 8 && portduino_config.mac_address.length() < 12) {
uint8_t hash[32] = {0}; uint8_t hash[32] = {0};
memcpy(hash, serial, 8); memcpy(hash, serial, 8);
crypto->hash(hash, 8); crypto->hash(hash, 8);
@@ -395,7 +368,7 @@ void portduinoSetup()
dmac[5] = hash[5]; dmac[5] = hash[5];
char macBuf[13] = {0}; char macBuf[13] = {0};
sprintf(macBuf, "%02X%02X%02X%02X%02X%02X", dmac[0], dmac[1], dmac[2], dmac[3], dmac[4], dmac[5]); sprintf(macBuf, "%02X%02X%02X%02X%02X%02X", dmac[0], dmac[1], dmac[2], dmac[3], dmac[4], dmac[5]);
settingsStrings[mac_address] = macBuf; portduino_config.mac_address = macBuf;
} }
} }
@@ -409,100 +382,38 @@ void portduinoSetup()
// Rather important to set this, if not running simulated. // Rather important to set this, if not running simulated.
randomSeed(time(NULL)); randomSeed(time(NULL));
std::string defaultGpioChipName = gpioChipName + std::to_string(settingsMap[default_gpiochip]); std::string defaultGpioChipName = gpioChipName + std::to_string(portduino_config.lora_default_gpiochip);
for (auto i : portduino_config.all_pins) {
for (configNames i : GPIO_lines) { if (i->enabled && i->pin > max_GPIO)
if (settingsMap.count(i) && settingsMap[i] > max_GPIO) max_GPIO = i->pin;
max_GPIO = settingsMap[i];
} }
gpioInit(max_GPIO + 1); // Done here so we can inform Portduino how many GPIOs we need. gpioInit(max_GPIO + 1); // Done here so we can inform Portduino how many GPIOs we need.
// Need to bind all the configured GPIO pins so they're not simulated // Need to bind all the configured GPIO pins so they're not simulated
// TODO: If one of these fails, we should log and terminate // TODO: If one of these fails, we should log and terminate
if (settingsMap.count(userButtonPin) > 0 && settingsMap[userButtonPin] != RADIOLIB_NC) { for (auto i : portduino_config.all_pins) {
if (initGPIOPin(settingsMap[userButtonPin], defaultGpioChipName, settingsMap[userButtonPin]) != ERRNO_OK) { if (i->enabled)
settingsMap[userButtonPin] = RADIOLIB_NC; if (initGPIOPin(i->pin, gpioChipName + std::to_string(i->gpiochip), i->line) != ERRNO_OK) {
} printf("Error setting pin number %d. It may not exist, or may already be in use.\n", i->line);
} exit(EXIT_FAILURE);
if (settingsMap.count(tbUpPin) > 0 && settingsMap[tbUpPin] != RADIOLIB_NC) { }
if (initGPIOPin(settingsMap[tbUpPin], defaultGpioChipName, settingsMap[tbUpPin]) != ERRNO_OK) {
settingsMap[tbUpPin] = RADIOLIB_NC;
}
}
if (settingsMap.count(tbDownPin) > 0 && settingsMap[tbDownPin] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[tbDownPin], defaultGpioChipName, settingsMap[tbDownPin]) != ERRNO_OK) {
settingsMap[tbDownPin] = RADIOLIB_NC;
}
}
if (settingsMap.count(tbLeftPin) > 0 && settingsMap[tbLeftPin] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[tbLeftPin], defaultGpioChipName, settingsMap[tbLeftPin]) != ERRNO_OK) {
settingsMap[tbLeftPin] = RADIOLIB_NC;
}
}
if (settingsMap.count(tbRightPin) > 0 && settingsMap[tbRightPin] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[tbRightPin], defaultGpioChipName, settingsMap[tbRightPin]) != ERRNO_OK) {
settingsMap[tbRightPin] = RADIOLIB_NC;
}
}
if (settingsMap.count(tbPressPin) > 0 && settingsMap[tbPressPin] != RADIOLIB_NC) {
if (initGPIOPin(settingsMap[tbPressPin], defaultGpioChipName, settingsMap[tbPressPin]) != ERRNO_OK) {
settingsMap[tbPressPin] = RADIOLIB_NC;
}
}
if (settingsMap[displayPanel] != no_screen) {
if (settingsMap[displayCS] > 0)
initGPIOPin(settingsMap[displayCS], defaultGpioChipName, settingsMap[displayCS]);
if (settingsMap[displayDC] > 0)
initGPIOPin(settingsMap[displayDC], defaultGpioChipName, settingsMap[displayDC]);
if (settingsMap[displayBacklight] > 0)
initGPIOPin(settingsMap[displayBacklight], defaultGpioChipName, settingsMap[displayBacklight]);
if (settingsMap[displayReset] > 0)
initGPIOPin(settingsMap[displayReset], defaultGpioChipName, settingsMap[displayReset]);
}
if (settingsMap[touchscreenModule] != no_touchscreen) {
if (settingsMap[touchscreenCS] > 0)
initGPIOPin(settingsMap[touchscreenCS], defaultGpioChipName, settingsMap[touchscreenCS]);
if (settingsMap[touchscreenIRQ] > 0)
initGPIOPin(settingsMap[touchscreenIRQ], defaultGpioChipName, settingsMap[touchscreenIRQ]);
} }
// Only initialize the radio pins when dealing with real, kernel controlled SPI hardware // Only initialize the radio pins when dealing with real, kernel controlled SPI hardware
if (settingsStrings[spidev] != "" && settingsStrings[spidev] != "ch341") { if (portduino_config.lora_spi_dev != "" && portduino_config.lora_spi_dev != "ch341") {
const struct { SPI.begin(portduino_config.lora_spi_dev.c_str());
configNames pin;
configNames gpiochip;
configNames line;
} pinMappings[] = {{cs_pin, cs_gpiochip, cs_line},
{irq_pin, irq_gpiochip, irq_line},
{busy_pin, busy_gpiochip, busy_line},
{reset_pin, reset_gpiochip, reset_line},
{rxen_pin, rxen_gpiochip, rxen_line},
{txen_pin, txen_gpiochip, txen_line},
{sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line}};
for (auto &pinMap : pinMappings) {
auto setMapIter = settingsMap.find(pinMap.pin);
if (setMapIter != settingsMap.end() && setMapIter->second != RADIOLIB_NC) {
if (initGPIOPin(setMapIter->second, gpioChipName + std::to_string(settingsMap[pinMap.gpiochip]),
settingsMap[pinMap.line]) != ERRNO_OK) {
printf("Error setting pin number %d. It may not exist, or may already be in use.\n",
settingsMap[pinMap.line]);
exit(EXIT_FAILURE);
}
}
}
SPI.begin(settingsStrings[spidev].c_str());
} }
if (settingsStrings[traceFilename] != "") { if (portduino_config.traceFilename != "") {
try { try {
traceFile.open(settingsStrings[traceFilename], std::ios::out | std::ios::app); traceFile.open(portduino_config.traceFilename, std::ios::out | std::ios::app);
} catch (std::ofstream::failure &e) { } catch (std::ofstream::failure &e) {
std::cout << "*** traceFile Exception " << e.what() << std::endl; std::cout << "*** traceFile Exception " << e.what() << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
if (verboseEnabled && settingsMap[logoutputlevel] != level_trace) { if (verboseEnabled && portduino_config.logoutputlevel != level_trace) {
settingsMap[logoutputlevel] = level_debug; portduino_config.logoutputlevel = level_debug;
} }
return; return;
@@ -537,99 +448,78 @@ bool loadConfig(const char *configPath)
yamlConfig = YAML::LoadFile(configPath); yamlConfig = YAML::LoadFile(configPath);
if (yamlConfig["Logging"]) { if (yamlConfig["Logging"]) {
if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "trace") { if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "trace") {
settingsMap[logoutputlevel] = level_trace; portduino_config.logoutputlevel = level_trace;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "debug") { } else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "debug") {
settingsMap[logoutputlevel] = level_debug; portduino_config.logoutputlevel = level_debug;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "info") { } else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "info") {
settingsMap[logoutputlevel] = level_info; portduino_config.logoutputlevel = level_info;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "warn") { } else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "warn") {
settingsMap[logoutputlevel] = level_warn; portduino_config.logoutputlevel = level_warn;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "error") { } else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "error") {
settingsMap[logoutputlevel] = level_error; portduino_config.logoutputlevel = level_error;
} }
settingsStrings[traceFilename] = yamlConfig["Logging"]["TraceFile"].as<std::string>(""); portduino_config.traceFilename = yamlConfig["Logging"]["TraceFile"].as<std::string>("");
if (yamlConfig["Logging"]["AsciiLogs"]) { if (yamlConfig["Logging"]["AsciiLogs"]) {
// Default is !isatty(1) but can be set explicitly in config.yaml // Default is !isatty(1) but can be set explicitly in config.yaml
settingsMap[ascii_logs] = yamlConfig["Logging"]["AsciiLogs"].as<bool>(); portduino_config.ascii_logs = yamlConfig["Logging"]["AsciiLogs"].as<bool>();
portduino_config.ascii_logs_explicit = true;
} }
} }
if (yamlConfig["Lora"]) { if (yamlConfig["Lora"]) {
const struct {
configNames cfgName;
std::string strName;
} loraModules[] = {{use_simradio, "sim"}, {use_autoconf, "auto"}, {use_rf95, "RF95"}, {use_sx1262, "sx1262"},
{use_sx1268, "sx1268"}, {use_sx1280, "sx1280"}, {use_lr1110, "lr1110"}, {use_lr1120, "lr1120"},
{use_lr1121, "lr1121"}, {use_llcc68, "LLCC68"}};
for (auto &loraModule : loraModules) {
settingsMap[loraModule.cfgName] = false;
}
if (yamlConfig["Lora"]["Module"]) { if (yamlConfig["Lora"]["Module"]) {
for (auto &loraModule : loraModules) { for (auto &loraModule : portduino_config.loraModules) {
if (yamlConfig["Lora"]["Module"].as<std::string>("") == loraModule.strName) { if (yamlConfig["Lora"]["Module"].as<std::string>("") == loraModule.second) {
settingsMap[loraModule.cfgName] = true; portduino_config.lora_module = loraModule.first;
break; break;
} }
} }
} }
if (yamlConfig["Lora"]["SX126X_MAX_POWER"])
portduino_config.sx126x_max_power = yamlConfig["Lora"]["SX126X_MAX_POWER"].as<int>(22);
if (yamlConfig["Lora"]["SX128X_MAX_POWER"])
portduino_config.sx128x_max_power = yamlConfig["Lora"]["SX128X_MAX_POWER"].as<int>(13);
if (yamlConfig["Lora"]["LR1110_MAX_POWER"])
portduino_config.lr1110_max_power = yamlConfig["Lora"]["LR1110_MAX_POWER"].as<int>(22);
if (yamlConfig["Lora"]["LR1120_MAX_POWER"])
portduino_config.lr1120_max_power = yamlConfig["Lora"]["LR1120_MAX_POWER"].as<int>(13);
if (yamlConfig["Lora"]["RF95_MAX_POWER"])
portduino_config.rf95_max_power = yamlConfig["Lora"]["RF95_MAX_POWER"].as<int>(20);
settingsMap[sx126x_max_power] = yamlConfig["Lora"]["SX126X_MAX_POWER"].as<int>(22); if (portduino_config.lora_module != use_autoconf && portduino_config.lora_module != use_simradio &&
settingsMap[sx128x_max_power] = yamlConfig["Lora"]["SX128X_MAX_POWER"].as<int>(13); !portduino_config.force_simradio) {
settingsMap[lr1110_max_power] = yamlConfig["Lora"]["LR1110_MAX_POWER"].as<int>(22); portduino_config.dio2_as_rf_switch = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as<bool>(false);
settingsMap[lr1120_max_power] = yamlConfig["Lora"]["LR1120_MAX_POWER"].as<int>(13); portduino_config.dio3_tcxo_voltage = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as<float>(0) * 1000;
settingsMap[rf95_max_power] = yamlConfig["Lora"]["RF95_MAX_POWER"].as<int>(20); if (portduino_config.dio3_tcxo_voltage == 0 && yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as<bool>(false)) {
portduino_config.dio3_tcxo_voltage = 1800; // default millivolts for "true"
}
settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as<bool>(false); // backwards API compatibility and to globally set gpiochip once
settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as<float>(0) * 1000; portduino_config.lora_default_gpiochip = yamlConfig["Lora"]["gpiochip"].as<int>(0);
if (settingsMap[dio3_tcxo_voltage] == 0 && yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as<bool>(false)) { for (auto this_pin : portduino_config.all_pins) {
settingsMap[dio3_tcxo_voltage] = 1800; // default millivolts for "true" if (this_pin->config_section == "Lora") {
} readGPIOFromYaml(yamlConfig["Lora"][this_pin->config_name], *this_pin);
}
// backwards API compatibility and to globally set gpiochip once
int defaultGpioChip = settingsMap[default_gpiochip] = yamlConfig["Lora"]["gpiochip"].as<int>(0);
const struct {
configNames pin;
configNames gpiochip;
configNames line;
std::string strName;
} pinMappings[] = {
{cs_pin, cs_gpiochip, cs_line, "CS"},
{irq_pin, irq_gpiochip, irq_line, "IRQ"},
{busy_pin, busy_gpiochip, busy_line, "Busy"},
{reset_pin, reset_gpiochip, reset_line, "Reset"},
{txen_pin, txen_gpiochip, txen_line, "TXen"},
{rxen_pin, rxen_gpiochip, rxen_line, "RXen"},
{sx126x_ant_sw_pin, sx126x_ant_sw_gpiochip, sx126x_ant_sw_line, "SX126X_ANT_SW"},
};
for (auto &pinMap : pinMappings) {
if (yamlConfig["Lora"][pinMap.strName].IsMap()) {
settingsMap[pinMap.pin] = yamlConfig["Lora"][pinMap.strName]["pin"].as<int>(RADIOLIB_NC);
settingsMap[pinMap.line] = yamlConfig["Lora"][pinMap.strName]["line"].as<int>(settingsMap[pinMap.pin]);
settingsMap[pinMap.gpiochip] = yamlConfig["Lora"][pinMap.strName]["gpiochip"].as<int>(defaultGpioChip);
} else { // backwards API compatibility
settingsMap[pinMap.pin] = yamlConfig["Lora"][pinMap.strName].as<int>(RADIOLIB_NC);
settingsMap[pinMap.line] = settingsMap[pinMap.pin];
settingsMap[pinMap.gpiochip] = defaultGpioChip;
} }
} }
settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as<int>(2000000); portduino_config.spiSpeed = yamlConfig["Lora"]["spiSpeed"].as<int>(2000000);
settingsStrings[lora_usb_serial_num] = yamlConfig["Lora"]["USB_Serialnum"].as<std::string>(""); portduino_config.lora_usb_serial_num = yamlConfig["Lora"]["USB_Serialnum"].as<std::string>("");
settingsMap[lora_usb_pid] = yamlConfig["Lora"]["USB_PID"].as<int>(0x5512); portduino_config.lora_usb_pid = yamlConfig["Lora"]["USB_PID"].as<int>(0x5512);
settingsMap[lora_usb_vid] = yamlConfig["Lora"]["USB_VID"].as<int>(0x1A86); portduino_config.lora_usb_vid = yamlConfig["Lora"]["USB_VID"].as<int>(0x1A86);
settingsStrings[spidev] = yamlConfig["Lora"]["spidev"].as<std::string>("spidev0.0"); portduino_config.lora_spi_dev = yamlConfig["Lora"]["spidev"].as<std::string>("spidev0.0");
if (settingsStrings[spidev] != "ch341") { if (portduino_config.lora_spi_dev != "ch341") {
settingsStrings[spidev] = "/dev/" + settingsStrings[spidev]; portduino_config.lora_spi_dev = "/dev/" + portduino_config.lora_spi_dev;
if (settingsStrings[spidev].length() == 14) { if (portduino_config.lora_spi_dev.length() == 14) {
int x = settingsStrings[spidev].at(11) - '0'; int x = portduino_config.lora_spi_dev.at(11) - '0';
int y = settingsStrings[spidev].at(13) - '0'; int y = portduino_config.lora_spi_dev.at(13) - '0';
// Pretty sure this is always true // Pretty sure this is always true
if (x >= 0 && x < 10 && y >= 0 && y < 10) { if (x >= 0 && x < 10 && y >= 0 && y < 10) {
// I believe this bit of weirdness is specifically for the new GUI // I believe this bit of weirdness is specifically for the new GUI
settingsMap[spidev] = x + y << 4; portduino_config.lora_spi_dev_int = x + y << 4;
settingsMap[displayspidev] = settingsMap[spidev]; portduino_config.display_spi_dev_int = portduino_config.lora_spi_dev_int;
settingsMap[touchscreenspidev] = settingsMap[spidev]; portduino_config.touchscreen_spi_dev_int = portduino_config.lora_spi_dev_int;
} }
} }
} }
@@ -676,163 +566,152 @@ bool loadConfig(const char *configPath)
} }
} }
} }
if (yamlConfig["GPIO"]) { readGPIOFromYaml(yamlConfig["GPIO"]["User"], portduino_config.userButtonPin);
settingsMap[userButtonPin] = yamlConfig["GPIO"]["User"].as<int>(RADIOLIB_NC);
}
if (yamlConfig["GPS"]) { if (yamlConfig["GPS"]) {
std::string serialPath = yamlConfig["GPS"]["SerialPath"].as<std::string>(""); std::string serialPath = yamlConfig["GPS"]["SerialPath"].as<std::string>("");
if (serialPath != "") { if (serialPath != "") {
Serial1.setPath(serialPath); Serial1.setPath(serialPath);
settingsMap[has_gps] = 1; portduino_config.has_gps = 1;
} }
} }
if (yamlConfig["I2C"]) { if (yamlConfig["I2C"]) {
settingsStrings[i2cdev] = yamlConfig["I2C"]["I2CDevice"].as<std::string>(""); portduino_config.i2cdev = yamlConfig["I2C"]["I2CDevice"].as<std::string>("");
} }
if (yamlConfig["Display"]) { if (yamlConfig["Display"]) {
if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7789")
settingsMap[displayPanel] = st7789; for (auto &screen_name : portduino_config.screen_names) {
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7735") if (yamlConfig["Display"]["Panel"].as<std::string>("") == screen_name.second)
settingsMap[displayPanel] = st7735; portduino_config.displayPanel = screen_name.first;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7735S") }
settingsMap[displayPanel] = st7735s; portduino_config.displayHeight = yamlConfig["Display"]["Height"].as<int>(0);
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7796") portduino_config.displayWidth = yamlConfig["Display"]["Width"].as<int>(0);
settingsMap[displayPanel] = st7796;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ILI9341") readGPIOFromYaml(yamlConfig["Display"]["DC"], portduino_config.displayDC, -1);
settingsMap[displayPanel] = ili9341; readGPIOFromYaml(yamlConfig["Display"]["CS"], portduino_config.displayCS, -1);
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ILI9342") readGPIOFromYaml(yamlConfig["Display"]["Backlight"], portduino_config.displayBacklight, -1);
settingsMap[displayPanel] = ili9342; readGPIOFromYaml(yamlConfig["Display"]["BacklightPWMChannel"], portduino_config.displayBacklightPWMChannel, -1);
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ILI9486") readGPIOFromYaml(yamlConfig["Display"]["Reset"], portduino_config.displayReset, -1);
settingsMap[displayPanel] = ili9486;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ILI9488") portduino_config.displayBacklightInvert = yamlConfig["Display"]["BacklightInvert"].as<bool>(false);
settingsMap[displayPanel] = ili9488; portduino_config.displayRGBOrder = yamlConfig["Display"]["RGBOrder"].as<bool>(false);
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "HX8357D") portduino_config.displayOffsetX = yamlConfig["Display"]["OffsetX"].as<int>(0);
settingsMap[displayPanel] = hx8357d; portduino_config.displayOffsetY = yamlConfig["Display"]["OffsetY"].as<int>(0);
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "X11") portduino_config.displayRotate = yamlConfig["Display"]["Rotate"].as<bool>(false);
settingsMap[displayPanel] = x11; portduino_config.displayOffsetRotate = yamlConfig["Display"]["OffsetRotate"].as<int>(1);
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "FB") portduino_config.displayInvert = yamlConfig["Display"]["Invert"].as<bool>(false);
settingsMap[displayPanel] = fb; portduino_config.displayBusFrequency = yamlConfig["Display"]["BusFrequency"].as<int>(40000000);
settingsMap[displayHeight] = yamlConfig["Display"]["Height"].as<int>(0);
settingsMap[displayWidth] = yamlConfig["Display"]["Width"].as<int>(0);
settingsMap[displayDC] = yamlConfig["Display"]["DC"].as<int>(-1);
settingsMap[displayCS] = yamlConfig["Display"]["CS"].as<int>(-1);
settingsMap[displayRGBOrder] = yamlConfig["Display"]["RGBOrder"].as<bool>(false);
settingsMap[displayBacklight] = yamlConfig["Display"]["Backlight"].as<int>(-1);
settingsMap[displayBacklightInvert] = yamlConfig["Display"]["BacklightInvert"].as<bool>(false);
settingsMap[displayBacklightPWMChannel] = yamlConfig["Display"]["BacklightPWMChannel"].as<int>(-1);
settingsMap[displayReset] = yamlConfig["Display"]["Reset"].as<int>(-1);
settingsMap[displayOffsetX] = yamlConfig["Display"]["OffsetX"].as<int>(0);
settingsMap[displayOffsetY] = yamlConfig["Display"]["OffsetY"].as<int>(0);
settingsMap[displayRotate] = yamlConfig["Display"]["Rotate"].as<bool>(false);
settingsMap[displayOffsetRotate] = yamlConfig["Display"]["OffsetRotate"].as<int>(1);
settingsMap[displayInvert] = yamlConfig["Display"]["Invert"].as<bool>(false);
settingsMap[displayBusFrequency] = yamlConfig["Display"]["BusFrequency"].as<int>(40000000);
if (yamlConfig["Display"]["spidev"]) { if (yamlConfig["Display"]["spidev"]) {
settingsStrings[displayspidev] = "/dev/" + yamlConfig["Display"]["spidev"].as<std::string>("spidev0.1"); portduino_config.display_spi_dev = "/dev/" + yamlConfig["Display"]["spidev"].as<std::string>("spidev0.1");
if (settingsStrings[displayspidev].length() == 14) { if (portduino_config.display_spi_dev.length() == 14) {
int x = settingsStrings[displayspidev].at(11) - '0'; int x = portduino_config.display_spi_dev.at(11) - '0';
int y = settingsStrings[displayspidev].at(13) - '0'; int y = portduino_config.display_spi_dev.at(13) - '0';
if (x >= 0 && x < 10 && y >= 0 && y < 10) { if (x >= 0 && x < 10 && y >= 0 && y < 10) {
settingsMap[displayspidev] = x + y << 4; portduino_config.display_spi_dev_int = x + y << 4;
settingsMap[touchscreenspidev] = settingsMap[displayspidev]; portduino_config.touchscreen_spi_dev_int = portduino_config.display_spi_dev_int;
} }
} }
} }
} }
if (yamlConfig["Touchscreen"]) { if (yamlConfig["Touchscreen"]) {
if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "XPT2046") if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "XPT2046")
settingsMap[touchscreenModule] = xpt2046; portduino_config.touchscreenModule = xpt2046;
else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "STMPE610") else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "STMPE610")
settingsMap[touchscreenModule] = stmpe610; portduino_config.touchscreenModule = stmpe610;
else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "GT911") else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "GT911")
settingsMap[touchscreenModule] = gt911; portduino_config.touchscreenModule = gt911;
else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "FT5x06") else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "FT5x06")
settingsMap[touchscreenModule] = ft5x06; portduino_config.touchscreenModule = ft5x06;
settingsMap[touchscreenCS] = yamlConfig["Touchscreen"]["CS"].as<int>(-1);
settingsMap[touchscreenIRQ] = yamlConfig["Touchscreen"]["IRQ"].as<int>(-1); readGPIOFromYaml(yamlConfig["Touchscreen"]["CS"], portduino_config.touchscreenCS, -1);
settingsMap[touchscreenBusFrequency] = yamlConfig["Touchscreen"]["BusFrequency"].as<int>(1000000); readGPIOFromYaml(yamlConfig["Touchscreen"]["IRQ"], portduino_config.touchscreenIRQ, -1);
settingsMap[touchscreenRotate] = yamlConfig["Touchscreen"]["Rotate"].as<int>(-1);
settingsMap[touchscreenI2CAddr] = yamlConfig["Touchscreen"]["I2CAddr"].as<int>(-1); portduino_config.touchscreenBusFrequency = yamlConfig["Touchscreen"]["BusFrequency"].as<int>(1000000);
portduino_config.touchscreenRotate = yamlConfig["Touchscreen"]["Rotate"].as<int>(-1);
portduino_config.touchscreenI2CAddr = yamlConfig["Touchscreen"]["I2CAddr"].as<int>(-1);
if (yamlConfig["Touchscreen"]["spidev"]) { if (yamlConfig["Touchscreen"]["spidev"]) {
settingsStrings[touchscreenspidev] = "/dev/" + yamlConfig["Touchscreen"]["spidev"].as<std::string>(""); portduino_config.touchscreen_spi_dev = "/dev/" + yamlConfig["Touchscreen"]["spidev"].as<std::string>("");
if (settingsStrings[touchscreenspidev].length() == 14) { if (portduino_config.touchscreen_spi_dev.length() == 14) {
int x = settingsStrings[touchscreenspidev].at(11) - '0'; int x = portduino_config.touchscreen_spi_dev.at(11) - '0';
int y = settingsStrings[touchscreenspidev].at(13) - '0'; int y = portduino_config.touchscreen_spi_dev.at(13) - '0';
if (x >= 0 && x < 10 && y >= 0 && y < 10) { if (x >= 0 && x < 10 && y >= 0 && y < 10) {
settingsMap[touchscreenspidev] = x + y << 4; portduino_config.touchscreen_spi_dev_int = x + y << 4;
} }
} }
} }
} }
if (yamlConfig["Input"]) { if (yamlConfig["Input"]) {
settingsStrings[keyboardDevice] = (yamlConfig["Input"]["KeyboardDevice"]).as<std::string>(""); portduino_config.keyboardDevice = (yamlConfig["Input"]["KeyboardDevice"]).as<std::string>("");
settingsStrings[pointerDevice] = (yamlConfig["Input"]["PointerDevice"]).as<std::string>(""); portduino_config.pointerDevice = (yamlConfig["Input"]["PointerDevice"]).as<std::string>("");
settingsMap[userButtonPin] = yamlConfig["Input"]["User"].as<int>(RADIOLIB_NC);
settingsMap[tbUpPin] = yamlConfig["Input"]["TrackballUp"].as<int>(RADIOLIB_NC); readGPIOFromYaml(yamlConfig["Input"]["User"], portduino_config.userButtonPin);
settingsMap[tbDownPin] = yamlConfig["Input"]["TrackballDown"].as<int>(RADIOLIB_NC); readGPIOFromYaml(yamlConfig["Input"]["TrackballUp"], portduino_config.tbUpPin);
settingsMap[tbLeftPin] = yamlConfig["Input"]["TrackballLeft"].as<int>(RADIOLIB_NC); readGPIOFromYaml(yamlConfig["Input"]["TrackballDown"], portduino_config.tbDownPin);
settingsMap[tbRightPin] = yamlConfig["Input"]["TrackballRight"].as<int>(RADIOLIB_NC); readGPIOFromYaml(yamlConfig["Input"]["TrackballLeft"], portduino_config.tbLeftPin);
settingsMap[tbPressPin] = yamlConfig["Input"]["TrackballPress"].as<int>(RADIOLIB_NC); readGPIOFromYaml(yamlConfig["Input"]["TrackballRight"], portduino_config.tbRightPin);
readGPIOFromYaml(yamlConfig["Input"]["TrackballPress"], portduino_config.tbPressPin);
if (yamlConfig["Input"]["TrackballDirection"].as<std::string>("RISING") == "RISING") { if (yamlConfig["Input"]["TrackballDirection"].as<std::string>("RISING") == "RISING") {
settingsMap[tbDirection] = 4; portduino_config.tbDirection = 4;
} else if (yamlConfig["Input"]["TrackballDirection"].as<std::string>("RISING") == "FALLING") { } else if (yamlConfig["Input"]["TrackballDirection"].as<std::string>("RISING") == "FALLING") {
settingsMap[tbDirection] = 3; portduino_config.tbDirection = 3;
} }
} }
if (yamlConfig["Webserver"]) { if (yamlConfig["Webserver"]) {
settingsMap[webserverport] = (yamlConfig["Webserver"]["Port"]).as<int>(-1); portduino_config.webserverport = (yamlConfig["Webserver"]["Port"]).as<int>(-1);
settingsStrings[webserverrootpath] = portduino_config.webserver_root_path =
(yamlConfig["Webserver"]["RootPath"]).as<std::string>("/usr/share/meshtasticd/web"); (yamlConfig["Webserver"]["RootPath"]).as<std::string>("/usr/share/meshtasticd/web");
settingsStrings[websslkeypath] = portduino_config.webserver_ssl_key_path =
(yamlConfig["Webserver"]["SSLKey"]).as<std::string>("/etc/meshtasticd/ssl/private_key.pem"); (yamlConfig["Webserver"]["SSLKey"]).as<std::string>("/etc/meshtasticd/ssl/private_key.pem");
settingsStrings[websslcertpath] = portduino_config.webserver_ssl_cert_path =
(yamlConfig["Webserver"]["SSLCert"]).as<std::string>("/etc/meshtasticd/ssl/certificate.pem"); (yamlConfig["Webserver"]["SSLCert"]).as<std::string>("/etc/meshtasticd/ssl/certificate.pem");
} }
if (yamlConfig["HostMetrics"]) { if (yamlConfig["HostMetrics"]) {
settingsMap[hostMetrics_channel] = (yamlConfig["HostMetrics"]["Channel"]).as<int>(0); portduino_config.hostMetrics_channel = (yamlConfig["HostMetrics"]["Channel"]).as<int>(0);
settingsMap[hostMetrics_interval] = (yamlConfig["HostMetrics"]["ReportInterval"]).as<int>(0); portduino_config.hostMetrics_interval = (yamlConfig["HostMetrics"]["ReportInterval"]).as<int>(0);
settingsStrings[hostMetrics_user_command] = (yamlConfig["HostMetrics"]["UserStringCommand"]).as<std::string>(""); portduino_config.hostMetrics_user_command = (yamlConfig["HostMetrics"]["UserStringCommand"]).as<std::string>("");
} }
if (yamlConfig["Config"]) { if (yamlConfig["Config"]) {
if (yamlConfig["Config"]["DisplayMode"]) { if (yamlConfig["Config"]["DisplayMode"]) {
settingsMap[has_configDisplayMode] = true; portduino_config.has_configDisplayMode = true;
if ((yamlConfig["Config"]["DisplayMode"]).as<std::string>("") == "TWOCOLOR") { if ((yamlConfig["Config"]["DisplayMode"]).as<std::string>("") == "TWOCOLOR") {
settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR; portduino_config.configDisplayMode = meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR;
} else if ((yamlConfig["Config"]["DisplayMode"]).as<std::string>("") == "INVERTED") { } else if ((yamlConfig["Config"]["DisplayMode"]).as<std::string>("") == "INVERTED") {
settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_INVERTED; portduino_config.configDisplayMode = meshtastic_Config_DisplayConfig_DisplayMode_INVERTED;
} else if ((yamlConfig["Config"]["DisplayMode"]).as<std::string>("") == "COLOR") { } else if ((yamlConfig["Config"]["DisplayMode"]).as<std::string>("") == "COLOR") {
settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_COLOR; portduino_config.configDisplayMode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR;
} else { } else {
settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT; portduino_config.configDisplayMode = meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT;
} }
} }
} }
if (yamlConfig["General"]) { if (yamlConfig["General"]) {
settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as<int>(200); portduino_config.MaxNodes = (yamlConfig["General"]["MaxNodes"]).as<int>(200);
settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as<int>(100); portduino_config.maxtophone = (yamlConfig["General"]["MaxMessageQueue"]).as<int>(100);
settingsStrings[config_directory] = (yamlConfig["General"]["ConfigDirectory"]).as<std::string>(""); portduino_config.config_directory = (yamlConfig["General"]["ConfigDirectory"]).as<std::string>("");
settingsStrings[available_directory] = portduino_config.available_directory =
(yamlConfig["General"]["AvailableDirectory"]).as<std::string>("/etc/meshtasticd/available.d/"); (yamlConfig["General"]["AvailableDirectory"]).as<std::string>("/etc/meshtasticd/available.d/");
if ((yamlConfig["General"]["MACAddress"]).as<std::string>("") != "" && if ((yamlConfig["General"]["MACAddress"]).as<std::string>("") != "" &&
(yamlConfig["General"]["MACAddressSource"]).as<std::string>("") != "") { (yamlConfig["General"]["MACAddressSource"]).as<std::string>("") != "") {
std::cout << "Cannot set both MACAddress and MACAddressSource!" << std::endl; std::cout << "Cannot set both MACAddress and MACAddressSource!" << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
settingsStrings[mac_address] = (yamlConfig["General"]["MACAddress"]).as<std::string>(""); portduino_config.mac_address = (yamlConfig["General"]["MACAddress"]).as<std::string>("");
if ((yamlConfig["General"]["MACAddressSource"]).as<std::string>("") != "") { if (portduino_config.mac_address != "") {
std::ifstream infile("/sys/class/net/" + (yamlConfig["General"]["MACAddressSource"]).as<std::string>("") + portduino_config.mac_address_explicit = true;
"/address"); } else if ((yamlConfig["General"]["MACAddressSource"]).as<std::string>("") != "") {
std::getline(infile, settingsStrings[mac_address]); portduino_config.mac_address_source = (yamlConfig["General"]["MACAddressSource"]).as<std::string>("");
std::ifstream infile("/sys/class/net/" + portduino_config.mac_address_source + "/address");
std::getline(infile, portduino_config.mac_address);
} }
// https://stackoverflow.com/a/20326454 // https://stackoverflow.com/a/20326454
settingsStrings[mac_address].erase( portduino_config.mac_address.erase(
std::remove(settingsStrings[mac_address].begin(), settingsStrings[mac_address].end(), ':'), std::remove(portduino_config.mac_address.begin(), portduino_config.mac_address.end(), ':'),
settingsStrings[mac_address].end()); portduino_config.mac_address.end());
} }
} catch (YAML::Exception &e) { } catch (YAML::Exception &e) {
std::cout << "*** Exception " << e.what() << std::endl; std::cout << "*** Exception " << e.what() << std::endl;
@@ -851,12 +730,12 @@ bool MAC_from_string(std::string mac_str, uint8_t *dmac)
{ {
mac_str.erase(std::remove(mac_str.begin(), mac_str.end(), ':'), mac_str.end()); mac_str.erase(std::remove(mac_str.begin(), mac_str.end(), ':'), mac_str.end());
if (mac_str.length() == 12) { if (mac_str.length() == 12) {
dmac[0] = std::stoi(settingsStrings[mac_address].substr(0, 2), nullptr, 16); dmac[0] = std::stoi(portduino_config.mac_address.substr(0, 2), nullptr, 16);
dmac[1] = std::stoi(settingsStrings[mac_address].substr(2, 2), nullptr, 16); dmac[1] = std::stoi(portduino_config.mac_address.substr(2, 2), nullptr, 16);
dmac[2] = std::stoi(settingsStrings[mac_address].substr(4, 2), nullptr, 16); dmac[2] = std::stoi(portduino_config.mac_address.substr(4, 2), nullptr, 16);
dmac[3] = std::stoi(settingsStrings[mac_address].substr(6, 2), nullptr, 16); dmac[3] = std::stoi(portduino_config.mac_address.substr(6, 2), nullptr, 16);
dmac[4] = std::stoi(settingsStrings[mac_address].substr(8, 2), nullptr, 16); dmac[4] = std::stoi(portduino_config.mac_address.substr(8, 2), nullptr, 16);
dmac[5] = std::stoi(settingsStrings[mac_address].substr(10, 2), nullptr, 16); dmac[5] = std::stoi(portduino_config.mac_address.substr(10, 2), nullptr, 16);
return true; return true;
} else { } else {
return false; return false;
@@ -875,4 +754,19 @@ std::string exec(const char *cmd)
result += buffer.data(); result += buffer.data();
} }
return result; return result;
}
void readGPIOFromYaml(YAML::Node sourceNode, pinMapping &destPin, int pinDefault)
{
if (sourceNode.IsMap()) {
destPin.enabled = true;
destPin.pin = sourceNode["pin"].as<int>(pinDefault);
destPin.line = sourceNode["line"].as<int>(destPin.pin);
destPin.gpiochip = sourceNode["gpiochip"].as<int>(portduino_config.lora_default_gpiochip);
} else if (sourceNode) { // backwards API compatibility
destPin.enabled = true;
destPin.pin = sourceNode.as<int>(pinDefault);
destPin.line = destPin.pin;
destPin.gpiochip = portduino_config.lora_default_gpiochip;
}
} }
+424 -94
View File
@@ -6,6 +6,7 @@
#include "LR11x0Interface.h" #include "LR11x0Interface.h"
#include "Module.h" #include "Module.h"
#include "platform/portduino/USBHal.h" #include "platform/portduino/USBHal.h"
#include "yaml-cpp/yaml.h"
// Product strings for auto-configuration // Product strings for auto-configuration
// {"PRODUCT_STRING", "CONFIG.YAML"} // {"PRODUCT_STRING", "CONFIG.YAML"}
@@ -19,36 +20,10 @@ inline const std::unordered_map<std::string, std::string> configProducts = {
{"RAK6421-13300-S1", "lora-RAK6421-13300-slot1.yaml"}, {"RAK6421-13300-S1", "lora-RAK6421-13300-slot1.yaml"},
{"RAK6421-13300-S2", "lora-RAK6421-13300-slot2.yaml"}}; {"RAK6421-13300-S2", "lora-RAK6421-13300-slot2.yaml"}};
enum configNames { enum screen_modules { no_screen, x11, fb, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9486, ili9488, hx8357d };
default_gpiochip, enum touchscreen_modules { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 };
cs_pin, enum portduino_log_level { level_error, level_warn, level_info, level_debug, level_trace };
cs_line, enum lora_module_enum {
cs_gpiochip,
irq_pin,
irq_line,
irq_gpiochip,
busy_pin,
busy_line,
busy_gpiochip,
reset_pin,
reset_line,
reset_gpiochip,
txen_pin,
txen_line,
txen_gpiochip,
rxen_pin,
rxen_line,
rxen_gpiochip,
sx126x_ant_sw_pin,
sx126x_ant_sw_line,
sx126x_ant_sw_gpiochip,
sx126x_max_power,
sx128x_max_power,
lr1110_max_power,
lr1120_max_power,
rf95_max_power,
dio2_as_rf_switch,
dio3_tcxo_voltage,
use_simradio, use_simradio,
use_autoconf, use_autoconf,
use_rf95, use_rf95,
@@ -58,72 +33,18 @@ enum configNames {
use_lr1110, use_lr1110,
use_lr1120, use_lr1120,
use_lr1121, use_lr1121,
use_llcc68, use_llcc68
lora_usb_serial_num, };
lora_usb_pid,
lora_usb_vid, struct pinMapping {
userButtonPin, std::string config_section;
tbUpPin, std::string config_name;
tbDownPin, int pin = RADIOLIB_NC;
tbLeftPin, int gpiochip;
tbRightPin, int line;
tbPressPin, bool enabled = false;
tbDirection,
spidev,
spiSpeed,
i2cdev,
has_gps,
touchscreenModule,
touchscreenCS,
touchscreenIRQ,
touchscreenI2CAddr,
touchscreenBusFrequency,
touchscreenRotate,
touchscreenspidev,
displayspidev,
displayBusFrequency,
displayPanel,
displayWidth,
displayHeight,
displayCS,
displayDC,
displayRGBOrder,
displayBacklight,
displayBacklightPWMChannel,
displayBacklightInvert,
displayReset,
displayRotate,
displayOffsetRotate,
displayOffsetX,
displayOffsetY,
displayInvert,
keyboardDevice,
pointerDevice,
logoutputlevel,
traceFilename,
webserver,
webserverport,
webserverrootpath,
websslkeypath,
websslcertpath,
maxtophone,
maxnodes,
ascii_logs,
config_directory,
available_directory,
mac_address,
hostMetrics_interval,
hostMetrics_channel,
hostMetrics_user_command,
configDisplayMode,
has_configDisplayMode
}; };
enum { no_screen, x11, fb, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9486, ili9488, hx8357d };
enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 };
enum { level_error, level_warn, level_info, level_debug, level_trace };
extern std::map<configNames, int> settingsMap;
extern std::map<configNames, std::string> settingsStrings;
extern std::ofstream traceFile; extern std::ofstream traceFile;
extern Ch341Hal *ch341Hal; extern Ch341Hal *ch341Hal;
int initGPIOPin(int pinNum, std::string gpioChipname, int line); int initGPIOPin(int pinNum, std::string gpioChipname, int line);
@@ -131,13 +52,422 @@ bool loadConfig(const char *configPath);
static bool ends_with(std::string_view str, std::string_view suffix); static bool ends_with(std::string_view str, std::string_view suffix);
void getMacAddr(uint8_t *dmac); void getMacAddr(uint8_t *dmac);
bool MAC_from_string(std::string mac_str, uint8_t *dmac); bool MAC_from_string(std::string mac_str, uint8_t *dmac);
void readGPIOFromYaml(YAML::Node sourceNode, pinMapping &destPin, int pinDefault = RADIOLIB_NC);
std::string exec(const char *cmd); std::string exec(const char *cmd);
extern struct portduino_config_struct { extern struct portduino_config_struct {
// Lora
std::map<lora_module_enum, std::string> loraModules = {
{use_simradio, "sim"}, {use_autoconf, "auto"}, {use_rf95, "RF95"}, {use_sx1262, "sx1262"}, {use_sx1268, "sx1268"},
{use_sx1280, "sx1280"}, {use_lr1110, "lr1110"}, {use_lr1120, "lr1120"}, {use_lr1121, "lr1121"}, {use_llcc68, "LLCC68"}};
std::map<screen_modules, std::string> screen_names = {{x11, "X11"}, {fb, "FB"}, {st7789, "ST7789"},
{st7735, "ST7735"}, {st7735s, "ST7735S"}, {st7796, "ST7796"},
{ili9341, "ILI9341"}, {ili9342, "ILI9342"}, {ili9486, "ILI9486"},
{ili9488, "ILI9488"}, {hx8357d, "HX8357D"}};
lora_module_enum lora_module;
bool has_rfswitch_table = false; bool has_rfswitch_table = false;
uint32_t rfswitch_dio_pins[5] = {RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC}; uint32_t rfswitch_dio_pins[5] = {RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
Module::RfSwitchMode_t rfswitch_table[8]; Module::RfSwitchMode_t rfswitch_table[8];
bool force_simradio = false; bool force_simradio = false;
bool has_device_id = false; bool has_device_id = false;
uint8_t device_id[16] = {0}; uint8_t device_id[16] = {0};
std::string lora_spi_dev = "";
std::string lora_usb_serial_num = "";
int lora_spi_dev_int = 0;
int lora_default_gpiochip = 0;
int sx126x_max_power = 22;
int sx128x_max_power = 13;
int lr1110_max_power = 22;
int lr1120_max_power = 13;
int rf95_max_power = 20;
bool dio2_as_rf_switch = false;
int dio3_tcxo_voltage = 0;
int lora_usb_pid = 0x5512;
int lora_usb_vid = 0x1A86;
int spiSpeed = 2000000;
pinMapping lora_cs_pin = {"Lora", "CS"};
pinMapping lora_irq_pin = {"Lora", "IRQ"};
pinMapping lora_busy_pin = {"Lora", "Busy"};
pinMapping lora_reset_pin = {"Lora", "Reset"};
pinMapping lora_txen_pin = {"Lora", "TXen"};
pinMapping lora_rxen_pin = {"Lora", "RXen"};
pinMapping lora_sx126x_ant_sw_pin = {"Lora", "SX126X_ANT_SW"};
// GPS
bool has_gps = false;
// I2C
std::string i2cdev = "";
// Display
std::string display_spi_dev = "";
int display_spi_dev_int = 0;
int displayBusFrequency = 40000000;
screen_modules displayPanel = no_screen;
int displayWidth = 0;
int displayHeight = 0;
bool displayRGBOrder = false;
bool displayBacklightInvert = false;
bool displayRotate = false;
int displayOffsetRotate = 1;
bool displayInvert = false;
int displayOffsetX = 0;
int displayOffsetY = 0;
pinMapping displayDC = {"Display", "DC"};
pinMapping displayCS = {"Display", "CS"};
pinMapping displayBacklight = {"Display", "Backlight"};
pinMapping displayBacklightPWMChannel = {"Display", "BacklightPWMChannel"};
pinMapping displayReset = {"Display", "Reset"};
// Touchscreen
std::string touchscreen_spi_dev = "";
int touchscreen_spi_dev_int = 0;
touchscreen_modules touchscreenModule = no_touchscreen;
int touchscreenI2CAddr = -1;
int touchscreenBusFrequency = 1000000;
int touchscreenRotate = -1;
pinMapping touchscreenCS = {"Touchscreen", "CS"};
pinMapping touchscreenIRQ = {"Touchscreen", "IRQ"};
// Input
std::string keyboardDevice = "";
std::string pointerDevice = "";
int tbDirection;
pinMapping userButtonPin = {"Input", "User"};
pinMapping tbUpPin = {"Input", "TrackballUp"};
pinMapping tbDownPin = {"Input", "TrackballDown"};
pinMapping tbLeftPin = {"Input", "TrackballLwft"};
pinMapping tbRightPin = {"Input", "TrackballRight"};
pinMapping tbPressPin = {"Input", "TrackballPress"};
// Logging
portduino_log_level logoutputlevel = level_debug;
std::string traceFilename;
bool ascii_logs = !isatty(1);
bool ascii_logs_explicit = false;
// Webserver
std::string webserver_root_path = "";
std::string webserver_ssl_key_path = "/etc/meshtasticd/ssl/private_key.pem";
std::string webserver_ssl_cert_path = "/etc/meshtasticd/ssl/certificate.pem";
int webserverport = -1;
// HostMetrics
std::string hostMetrics_user_command = "";
int hostMetrics_interval = 0;
int hostMetrics_channel = 0;
// config
int configDisplayMode = 0;
bool has_configDisplayMode = false;
// General
std::string mac_address = "";
bool mac_address_explicit = false;
std::string mac_address_source = "";
std::string config_directory = "";
std::string available_directory = "/etc/meshtasticd/available.d/";
int maxtophone = 100;
int MaxNodes = 200;
pinMapping *all_pins[20] = {&lora_cs_pin,
&lora_irq_pin,
&lora_busy_pin,
&lora_reset_pin,
&lora_txen_pin,
&lora_rxen_pin,
&lora_sx126x_ant_sw_pin,
&displayDC,
&displayCS,
&displayBacklight,
&displayBacklightPWMChannel,
&displayReset,
&touchscreenCS,
&touchscreenIRQ,
&userButtonPin,
&tbUpPin,
&tbDownPin,
&tbLeftPin,
&tbRightPin,
&tbPressPin};
std::string emit_yaml()
{
YAML::Emitter out;
out << YAML::BeginMap;
// Lora
out << YAML::Key << "Lora" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "Module" << YAML::Value << loraModules[lora_module];
for (auto lora_pin : all_pins) {
if (lora_pin->config_section == "Lora" && lora_pin->enabled) {
out << YAML::Key << lora_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << lora_pin->pin;
out << YAML::Key << "line" << YAML::Value << lora_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << lora_pin->gpiochip;
out << YAML::EndMap; // User
}
}
if (sx126x_max_power != 22)
out << YAML::Key << "SX126X_MAX_POWER" << YAML::Value << sx126x_max_power;
if (sx128x_max_power != 13)
out << YAML::Key << "SX128X_MAX_POWER" << YAML::Value << sx128x_max_power;
if (lr1110_max_power != 22)
out << YAML::Key << "LR1110_MAX_POWER" << YAML::Value << lr1110_max_power;
if (lr1120_max_power != 13)
out << YAML::Key << "LR1120_MAX_POWER" << YAML::Value << lr1120_max_power;
if (rf95_max_power != 20)
out << YAML::Key << "RF95_MAX_POWER" << YAML::Value << rf95_max_power;
out << YAML::Key << "DIO2_AS_RF_SWITCH" << YAML::Value << dio2_as_rf_switch;
if (dio3_tcxo_voltage != 0)
out << YAML::Key << "DIO3_TCXO_VOLTAGE" << YAML::Value << dio3_tcxo_voltage;
if (lora_usb_pid != 0x5512)
out << YAML::Key << "USB_PID" << YAML::Value << YAML::Hex << lora_usb_pid;
if (lora_usb_vid != 0x1A86)
out << YAML::Key << "USB_VID" << YAML::Value << YAML::Hex << lora_usb_vid;
if (lora_spi_dev != "")
out << YAML::Key << "spidev" << YAML::Value << lora_spi_dev;
if (lora_usb_serial_num != "")
out << YAML::Key << "USB_Serialnum" << YAML::Value << lora_usb_serial_num;
out << YAML::Key << "spiSpeed" << YAML::Value << spiSpeed;
if (rfswitch_dio_pins[0] != RADIOLIB_NC) {
out << YAML::Key << "rfswitch_table" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pins";
out << YAML::Value << YAML::Flow << YAML::BeginSeq;
for (int i = 0; i < 5; i++) {
// set up the pin array first
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO5)
out << "DIO5";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO6)
out << "DIO6";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO7)
out << "DIO7";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO8)
out << "DIO8";
if (rfswitch_dio_pins[i] == RADIOLIB_LR11X0_DIO10)
out << "DIO10";
}
out << YAML::EndSeq;
for (int i = 0; i < 7; i++) {
switch (i) {
case 0:
out << YAML::Key << "MODE_STBY";
break;
case 1:
out << YAML::Key << "MODE_RX";
break;
case 2:
out << YAML::Key << "MODE_TX";
break;
case 3:
out << YAML::Key << "MODE_TX_HP";
break;
case 4:
out << YAML::Key << "MODE_TX_HF";
break;
case 5:
out << YAML::Key << "MODE_GNSS";
break;
case 6:
out << YAML::Key << "MODE_WIFI";
break;
}
out << YAML::Value << YAML::Flow << YAML::BeginSeq;
for (int j = 0; j < 5; j++) {
if (rfswitch_table[i].values[j] == HIGH) {
out << "HIGH";
} else {
out << "LOW";
}
}
out << YAML::EndSeq;
}
out << YAML::EndMap; // rfswitch_table
}
out << YAML::EndMap; // Lora
if (i2cdev != "") {
out << YAML::Key << "I2C" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "I2CDevice" << YAML::Value << i2cdev;
out << YAML::EndMap; // I2C
}
// Display
if (displayPanel != no_screen) {
out << YAML::Key << "Display" << YAML::Value << YAML::BeginMap;
for (auto &screen_name : screen_names) {
if (displayPanel == screen_name.first)
out << YAML::Key << "Module" << YAML::Value << screen_name.second;
}
for (auto display_pin : all_pins) {
if (display_pin->config_section == "Display" && display_pin->enabled) {
out << YAML::Key << display_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << display_pin->pin;
out << YAML::Key << "line" << YAML::Value << display_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << display_pin->gpiochip;
out << YAML::EndMap;
}
}
out << YAML::Key << "spidev" << YAML::Value << display_spi_dev;
out << YAML::Key << "BusFrequency" << YAML::Value << displayBusFrequency;
if (displayWidth)
out << YAML::Key << "Width" << YAML::Value << displayWidth;
if (displayHeight)
out << YAML::Key << "Height" << YAML::Value << displayHeight;
if (displayRGBOrder)
out << YAML::Key << "RGBOrder" << YAML::Value << true;
if (displayBacklightInvert)
out << YAML::Key << "BacklightInvert" << YAML::Value << true;
if (displayRotate)
out << YAML::Key << "Rotate" << YAML::Value << true;
if (displayInvert)
out << YAML::Key << "Invert" << YAML::Value << true;
if (displayOffsetX)
out << YAML::Key << "OffsetX" << YAML::Value << displayOffsetX;
if (displayOffsetY)
out << YAML::Key << "OffsetY" << YAML::Value << displayOffsetY;
out << YAML::Key << "OffsetRotate" << YAML::Value << displayOffsetRotate;
out << YAML::EndMap; // Display
}
// Touchscreen
if (touchscreen_spi_dev != "") {
out << YAML::Key << "Touchscreen" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "spidev" << YAML::Value << touchscreen_spi_dev;
out << YAML::Key << "BusFrequency" << YAML::Value << touchscreenBusFrequency;
switch (touchscreenModule) {
case xpt2046:
out << YAML::Key << "Module" << YAML::Value << "XPT2046";
case stmpe610:
out << YAML::Key << "Module" << YAML::Value << "STMPE610";
case gt911:
out << YAML::Key << "Module" << YAML::Value << "GT911";
case ft5x06:
out << YAML::Key << "Module" << YAML::Value << "FT5x06";
}
for (auto touchscreen_pin : all_pins) {
if (touchscreen_pin->config_section == "Touchscreen" && touchscreen_pin->enabled) {
out << YAML::Key << touchscreen_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << touchscreen_pin->pin;
out << YAML::Key << "line" << YAML::Value << touchscreen_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << touchscreen_pin->gpiochip;
out << YAML::EndMap;
}
}
if (touchscreenRotate != -1)
out << YAML::Key << "Rotate" << YAML::Value << touchscreenRotate;
if (touchscreenI2CAddr != -1)
out << YAML::Key << "I2CAddr" << YAML::Value << touchscreenI2CAddr;
out << YAML::EndMap; // Touchscreen
}
// Input
out << YAML::Key << "Input" << YAML::Value << YAML::BeginMap;
if (keyboardDevice != "")
out << YAML::Key << "KeyboardDevice" << YAML::Value << keyboardDevice;
if (pointerDevice != "")
out << YAML::Key << "PointerDevice" << YAML::Value << pointerDevice;
for (auto input_pin : all_pins) {
if (input_pin->config_section == "Input" && input_pin->enabled) {
out << YAML::Key << input_pin->config_name << YAML::Value << YAML::BeginMap;
out << YAML::Key << "pin" << YAML::Value << input_pin->pin;
out << YAML::Key << "line" << YAML::Value << input_pin->line;
out << YAML::Key << "gpiochip" << YAML::Value << input_pin->gpiochip;
out << YAML::EndMap;
}
}
if (tbDirection == 3)
out << YAML::Key << "TrackballDirection" << YAML::Value << "FALLING";
out << YAML::EndMap; // Input
out << YAML::Key << "Logging" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "LogLevel" << YAML::Value;
switch (logoutputlevel) {
case level_error:
out << "error";
break;
case level_warn:
out << "warn";
break;
case level_info:
out << "info";
break;
case level_debug:
out << "debug";
break;
case level_trace:
out << "trace";
break;
}
if (traceFilename != "")
out << YAML::Key << "TraceFile" << YAML::Value << traceFilename;
if (ascii_logs_explicit) {
out << YAML::Key << "AsciiLogs" << YAML::Value << ascii_logs;
}
out << YAML::EndMap; // Logging
// Webserver
if (webserver_root_path != "") {
out << YAML::Key << "Webserver" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "RootPath" << YAML::Value << webserver_root_path;
out << YAML::Key << "SSLKey" << YAML::Value << webserver_ssl_key_path;
out << YAML::Key << "SSLCert" << YAML::Value << webserver_ssl_cert_path;
out << YAML::Key << "Port" << YAML::Value << webserverport;
out << YAML::EndMap; // Webserver
}
// HostMetrics
if (hostMetrics_user_command != "") {
out << YAML::Key << "HostMetrics" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "UserStringCommand" << YAML::Value << hostMetrics_user_command;
out << YAML::Key << "ReportInterval" << YAML::Value << hostMetrics_interval;
out << YAML::Key << "Channel" << YAML::Value << hostMetrics_channel;
out << YAML::EndMap; // HostMetrics
}
// config
if (has_configDisplayMode) {
out << YAML::Key << "Config" << YAML::Value << YAML::BeginMap;
switch (configDisplayMode) {
case meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR:
out << YAML::Key << "DisplayMode" << YAML::Value << "TWOCOLOR";
case meshtastic_Config_DisplayConfig_DisplayMode_INVERTED:
out << YAML::Key << "DisplayMode" << YAML::Value << "INVERTED";
case meshtastic_Config_DisplayConfig_DisplayMode_COLOR:
out << YAML::Key << "DisplayMode" << YAML::Value << "COLOR";
case meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT:
out << YAML::Key << "DisplayMode" << YAML::Value << "DEFAULT";
}
out << YAML::EndMap; // Config
}
// General
out << YAML::Key << "General" << YAML::Value << YAML::BeginMap;
if (config_directory != "")
out << YAML::Key << "ConfigDirectory" << YAML::Value << config_directory;
if (mac_address_explicit)
out << YAML::Key << "MACAddress" << YAML::Value << mac_address;
if (mac_address_source != "")
out << YAML::Key << "MACAddressSource" << YAML::Value << mac_address_source;
if (available_directory != "")
out << YAML::Key << "AvailableDirectory" << YAML::Value << available_directory;
out << YAML::Key << "MaxMessageQueue" << YAML::Value << maxtophone;
out << YAML::Key << "MaxNodes" << YAML::Value << MaxNodes;
out << YAML::EndMap; // General
return out.c_str();
}
} portduino_config; } portduino_config;
+5 -5
View File
@@ -28,9 +28,9 @@
#endif #endif
#ifndef HAS_TRACKBALL #ifndef HAS_TRACKBALL
#define HAS_TRACKBALL 1 #define HAS_TRACKBALL 1
#define TB_DOWN (uint8_t) settingsMap[tbDownPin] #define TB_DOWN (uint8_t) portduino_config.tbDownPin.pin
#define TB_UP (uint8_t) settingsMap[tbUpPin] #define TB_UP (uint8_t) portduino_config.tbUpPin.pin
#define TB_LEFT (uint8_t) settingsMap[tbLeftPin] #define TB_LEFT (uint8_t) portduino_config.tbLeftPin.pin
#define TB_RIGHT (uint8_t) settingsMap[tbRightPin] #define TB_RIGHT (uint8_t) portduino_config.tbRightPin.pin
#define TB_PRESS (uint8_t) settingsMap[tbPressPin] #define TB_PRESS (uint8_t) portduino_config.tbPressPin.pin
#endif #endif
-17
View File
@@ -431,15 +431,6 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_LOW_LEVEL); // pmu irq gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_LOW_LEVEL); // pmu irq
#endif #endif
#ifdef T_LORA_PAGER
LOG_DEBUG("power down XL9555 io");
io.digitalWrite(EXPANDS_DRV_EN, LOW);
io.digitalWrite(EXPANDS_AMP_EN, LOW);
io.digitalWrite(EXPANDS_KB_EN, LOW);
io.digitalWrite(EXPANDS_SD_EN, LOW);
io.digitalWrite(EXPANDS_GPIO_EN, LOW);
#endif
auto res = esp_sleep_enable_gpio_wakeup(); auto res = esp_sleep_enable_gpio_wakeup();
if (res != ESP_OK) { if (res != ESP_OK) {
LOG_ERROR("esp_sleep_enable_gpio_wakeup result %d", res); LOG_ERROR("esp_sleep_enable_gpio_wakeup result %d", res);
@@ -480,14 +471,6 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
gpio_wakeup_disable((gpio_num_t)RF95_IRQ); gpio_wakeup_disable((gpio_num_t)RF95_IRQ);
} }
#endif #endif
#ifdef T_LORA_PAGER
LOG_DEBUG("power up XL9555 io");
io.digitalWrite(EXPANDS_DRV_EN, HIGH);
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
io.digitalWrite(EXPANDS_KB_EN, HIGH);
io.digitalWrite(EXPANDS_SD_EN, HIGH);
io.digitalWrite(EXPANDS_GPIO_EN, HIGH);
#endif
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
notifyLightSleepEnd.notifyObservers(cause); // Button interrupts are reattached here notifyLightSleepEnd.notifyObservers(cause); // Button interrupts are reattached here
@@ -1,30 +1,7 @@
#include "../test_helpers.h" #include "../test_helpers.h"
// test data initialization // Helper function for all encrypted packet assertions
const int from = 0x11223344; void assert_encrypted_packet(const std::string &json, meshtastic_MeshPacket packet)
const int to = 0x55667788;
const int id = 0x9999;
// Helper function to create a test encrypted packet
meshtastic_MeshPacket create_test_encrypted_packet(uint32_t from, uint32_t to, uint32_t id, const char *data)
{
meshtastic_MeshPacket packet = meshtastic_MeshPacket_init_zero;
packet.from = from;
packet.to = to;
packet.id = id;
packet.which_payload_variant = meshtastic_MeshPacket_encrypted_tag;
if (data) {
packet.encrypted.size = strlen(data);
memcpy(packet.encrypted.bytes, data, packet.encrypted.size);
}
return packet;
}
// Comprehensive helper function for all encrypted packet assertions
void assert_encrypted_packet(const std::string &json, uint32_t expected_from, uint32_t expected_to, uint32_t expected_id,
size_t expected_size)
{ {
// Parse and validate JSON // Parse and validate JSON
TEST_ASSERT_TRUE(json.length() > 0); TEST_ASSERT_TRUE(json.length() > 0);
@@ -37,24 +14,24 @@ void assert_encrypted_packet(const std::string &json, uint32_t expected_from, ui
// Assert basic packet fields // Assert basic packet fields
TEST_ASSERT_TRUE(jsonObj.find("from") != jsonObj.end()); TEST_ASSERT_TRUE(jsonObj.find("from") != jsonObj.end());
TEST_ASSERT_EQUAL(expected_from, (uint32_t)jsonObj.at("from")->AsNumber()); TEST_ASSERT_EQUAL(packet.from, (uint32_t)jsonObj.at("from")->AsNumber());
TEST_ASSERT_TRUE(jsonObj.find("to") != jsonObj.end()); TEST_ASSERT_TRUE(jsonObj.find("to") != jsonObj.end());
TEST_ASSERT_EQUAL(expected_to, (uint32_t)jsonObj.at("to")->AsNumber()); TEST_ASSERT_EQUAL(packet.to, (uint32_t)jsonObj.at("to")->AsNumber());
TEST_ASSERT_TRUE(jsonObj.find("id") != jsonObj.end()); TEST_ASSERT_TRUE(jsonObj.find("id") != jsonObj.end());
TEST_ASSERT_EQUAL(expected_id, (uint32_t)jsonObj.at("id")->AsNumber()); TEST_ASSERT_EQUAL(packet.id, (uint32_t)jsonObj.at("id")->AsNumber());
// Assert encrypted data fields // Assert encrypted data fields
TEST_ASSERT_TRUE(jsonObj.find("bytes") != jsonObj.end()); TEST_ASSERT_TRUE(jsonObj.find("bytes") != jsonObj.end());
TEST_ASSERT_TRUE(jsonObj.at("bytes")->IsString()); TEST_ASSERT_TRUE(jsonObj.at("bytes")->IsString());
TEST_ASSERT_TRUE(jsonObj.find("size") != jsonObj.end()); TEST_ASSERT_TRUE(jsonObj.find("size") != jsonObj.end());
TEST_ASSERT_EQUAL(expected_size, (int)jsonObj.at("size")->AsNumber()); TEST_ASSERT_EQUAL(packet.encrypted.size, (int)jsonObj.at("size")->AsNumber());
// Assert hex encoding // Assert hex encoding
std::string encrypted_hex = jsonObj["bytes"]->AsString(); std::string encrypted_hex = jsonObj["bytes"]->AsString();
TEST_ASSERT_EQUAL(expected_size * 2, encrypted_hex.length()); TEST_ASSERT_EQUAL(packet.encrypted.size * 2, encrypted_hex.length());
delete root; delete root;
} }
@@ -63,20 +40,20 @@ void assert_encrypted_packet(const std::string &json, uint32_t expected_from, ui
void test_encrypted_packet_serialization() void test_encrypted_packet_serialization()
{ {
const char *data = "encrypted_payload_data"; const char *data = "encrypted_payload_data";
meshtastic_MeshPacket packet =
meshtastic_MeshPacket packet = create_test_encrypted_packet(from, to, id, data); create_test_packet(meshtastic_PortNum_TEXT_MESSAGE_APP, reinterpret_cast<const uint8_t *>(data), strlen(data),
meshtastic_MeshPacket_encrypted_tag);
std::string json = MeshPacketSerializer::JsonSerializeEncrypted(&packet); std::string json = MeshPacketSerializer::JsonSerializeEncrypted(&packet);
assert_encrypted_packet(json, from, to, id, strlen(data)); assert_encrypted_packet(json, packet);
} }
// Test empty encrypted packet // Test empty encrypted packet
void test_empty_encrypted_packet() void test_empty_encrypted_packet()
{ {
const char *data = ""; meshtastic_MeshPacket packet =
create_test_packet(meshtastic_PortNum_TEXT_MESSAGE_APP, nullptr, 0, meshtastic_MeshPacket_encrypted_tag);
meshtastic_MeshPacket packet = create_test_encrypted_packet(from, to, id, data);
std::string json = MeshPacketSerializer::JsonSerializeEncrypted(&packet); std::string json = MeshPacketSerializer::JsonSerializeEncrypted(&packet);
assert_encrypted_packet(json, from, to, id, strlen(data)); assert_encrypted_packet(json, packet);
} }
@@ -11,7 +11,8 @@
#include <unity.h> #include <unity.h>
// Helper function to create a test packet with the given port and payload // Helper function to create a test packet with the given port and payload
static meshtastic_MeshPacket create_test_packet(meshtastic_PortNum port, const uint8_t *payload, size_t payload_size) static meshtastic_MeshPacket create_test_packet(meshtastic_PortNum port, const uint8_t *payload, size_t payload_size,
int payload_variant = meshtastic_MeshPacket_decoded_tag)
{ {
meshtastic_MeshPacket packet = meshtastic_MeshPacket_init_zero; meshtastic_MeshPacket packet = meshtastic_MeshPacket_init_zero;
@@ -29,8 +30,12 @@ static meshtastic_MeshPacket create_test_packet(meshtastic_PortNum port, const u
packet.delayed = meshtastic_MeshPacket_Delayed_NO_DELAY; packet.delayed = meshtastic_MeshPacket_Delayed_NO_DELAY;
// Set decoded variant // Set decoded variant
packet.which_payload_variant = meshtastic_MeshPacket_decoded_tag; packet.which_payload_variant = payload_variant;
packet.decoded.portnum = port; packet.decoded.portnum = port;
if (payload_variant == meshtastic_MeshPacket_encrypted_tag && payload) {
packet.encrypted.size = payload_size;
memcpy(packet.encrypted.bytes, payload, packet.encrypted.size);
}
memcpy(packet.decoded.payload.bytes, payload, payload_size); memcpy(packet.decoded.payload.bytes, payload, payload_size);
packet.decoded.payload.size = payload_size; packet.decoded.payload.size = payload_size;
packet.decoded.want_response = false; packet.decoded.want_response = false;
@@ -0,0 +1,12 @@
; 9M2IBR APRS LoRa Tracker: ESP32-WROOM-32 + EBYTE E22-400M30S
; https://shopee.com.my/product/1095224/21692283917
[env:9m2ibr_aprs_lora_tracker]
extends = esp32_base
board = esp32doit-devkit-v1
board_level = extra
build_flags =
${esp32_base.build_flags}
-D PRIVATE_HW
-D EBYTE_E22
-D EBYTE_E22_900M30S ; Assume Tx power curve is identical to 900M30S as there is no documentation
-I variants/esp32/diy/9m2ibr_aprs_lora_tracker
@@ -0,0 +1,74 @@
/*
9M2IBR APRS LoRa Tracker: ESP32-WROOM-32 + EBYTE E22-400M30S
https://shopee.com.my/product/1095224/21692283917
Originally developed for LoRa_APRS_iGate and GPIO is similar to
https://github.com/richonguzman/LoRa_APRS_iGate/blob/main/variants/ESP32_DIY_1W_LoRa_Mesh_V1_2/board_pinout.h
*/
// OLED (may be different controllers depending on screen size)
#define I2C_SDA 21
#define I2C_SCL 22
#define HAS_SCREEN 1 // Generates randomized BLE pin
// GNSS: Ai-Thinker GP-02 BDS/GNSS module
#define GPS_RX_PIN 16
#define GPS_TX_PIN 17
// Button
#define BUTTON_PIN 15 // Right side button - if not available, set device.button_gpio to 0 from Meshtastic client
// LEDs
#define LED_PIN 13 // Tx LED
#define USER_LED 2 // Rx LED
// Buzzer
#define PIN_BUZZER 33
// Battery sense
#define BATTERY_PIN 35
#define ADC_MULTIPLIER 2.01 // 100k + 100k, and add 1% tolerance
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
#define BATTERY_SENSE_RESOLUTION_BITS ADC_RESOLUTION
// SPI
#define LORA_SCK 18
#define LORA_MISO 19
#define LORA_MOSI 23
// LoRa
#define LORA_CS 5
#define LORA_DIO0 26 // a No connect on the SX1262/SX1268 module
#define LORA_RESET 27 // RST for SX1276, and for SX1262/SX1268
#define LORA_DIO1 12 // IRQ for SX1262/SX1268
#define LORA_DIO2 RADIOLIB_NC // BUSY for SX1262/SX1268
#define LORA_DIO3 // NC, but used as TCXO supply by E22 module
#define LORA_RXEN 32 // RF switch RX (and E22 LNA) control by ESP32 GPIO
#define LORA_TXEN 25 // RF switch TX (and E22 PA) control by ESP32 GPIO
// RX/TX for RFM95/SX127x
#define RF95_RXEN LORA_RXEN
#define RF95_TXEN LORA_TXEN
// #define RF95_TCXO <GPIO#>
// common pinouts for SX126X modules
#define SX126X_CS 5
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
#define SX126X_RXEN LORA_RXEN
#define SX126X_TXEN LORA_TXEN
// Support alternative modules if soldered in place of E22
#define USE_RF95 // RFM95/SX127x
#define USE_SX1262
#define USE_SX1268
#define USE_LLCC68
// E22 TCXO support
#ifdef EBYTE_E22
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL
#endif
@@ -1,6 +1,7 @@
[env:heltec-wireless-bridge] [env:heltec-wireless-bridge]
;build_type = debug ; to make it possible to step through our jtag debugger ;build_type = debug ; to make it possible to step through our jtag debugger
extends = esp32_base extends = esp32_base
board_level = extra
board = heltec_wifi_lora_32 board = heltec_wifi_lora_32
build_flags = build_flags =
${esp32_base.build_flags} ${esp32_base.build_flags}
+1
View File
@@ -1,5 +1,6 @@
[env:trackerd] [env:trackerd]
extends = esp32_base extends = esp32_base
board_level = extra
board = pico32 board = pico32
board_build.f_flash = 80000000L board_build.f_flash = 80000000L
@@ -2,7 +2,7 @@
extends = esp32s3_base extends = esp32s3_base
board = bpi_picow_esp32_s3 board = bpi_picow_esp32_s3
board_check = true board_check = true
board_build.partitions = default_8MB.csv board_build.partitions = partition-table-8MB.csv
;OpenOCD flash method ;OpenOCD flash method
;upload_protocol = esp-builtin ;upload_protocol = esp-builtin
;Normal method ;Normal method
@@ -26,6 +26,7 @@ extends = env:picomputer-s3
build_flags = build_flags =
${env:picomputer-s3.build_flags} ${env:picomputer-s3.build_flags}
-D MESHTASTIC_EXCLUDE_WEBSERVER=1
-D INPUTDRIVER_MATRIX_TYPE=1 -D INPUTDRIVER_MATRIX_TYPE=1
-D USE_PIN_BUZZER=PIN_BUZZER -D USE_PIN_BUZZER=PIN_BUZZER
-D USE_SX127x -D USE_SX127x
@@ -0,0 +1,28 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include "variant.h"
#include <stdint.h>
#define USB_VID 0x303a
#define USB_PID 0x1001
// The default Wire will be mapped to PMU and RTC
static const uint8_t SDA = 9;
static const uint8_t SCL = 40;
// Default SPI will be mapped to Radio
static const uint8_t SS = 12;
static const uint8_t MOSI = 11;
static const uint8_t MISO = 10;
static const uint8_t SCK = 13;
#define SPI_MOSI (11)
#define SPI_SCK (13)
#define SPI_MISO (10)
#define SPI_CS (12)
// LEDs
#define LED_BUILTIN LED_GREEN
#endif /* Pins_Arduino_h */
@@ -0,0 +1,88 @@
; rak_wismeshtap2 rak3112
[rak_wismeshtap_s3]
extends = esp32s3_base
board = wiscore_rak3312
board_check = true
upload_protocol = esptool
board_build.partitions = default_8MB.csv
build_flags =
${esp32_base.build_flags}
-D RAK3312
-D RAK_WISMESH_TAP_V2
-I variants/esp32s3/rak_wismesh_tap_v2
lib_deps =
${esp32s3_base.lib_deps}
lovyan03/LovyanGFX@^1.2.0
[ft5x06]
extends = mesh_tab_base
build_flags =
-D LGFX_TOUCH=FT5x06
-D LGFX_TOUCH_I2C_FREQ=100000
-D LGFX_TOUCH_I2C_PORT=0
-D LGFX_TOUCH_I2C_ADDR=0x38
-D LGFX_TOUCH_I2C_SDA=9
-D LGFX_TOUCH_I2C_SCL=40
-D LGFX_TOUCH_RST=-1
-D LGFX_TOUCH_INT=39
[env:rak_wismesh_tap_v2-tft]
extends = rak_wismeshtap_s3
build_flags =
${rak_wismeshtap_s3.build_flags}
-D CONFIG_ARDUHAL_ESP_LOG
-D CONFIG_ARDUHAL_LOG_COLORS=1
-D CONFIG_DISABLE_HAL_LOCKS=1
-D LV_LVGL_H_INCLUDE_SIMPLE
-D LV_CONF_INCLUDE_SIMPLE
-D LV_COMP_CONF_INCLUDE_SIMPLE
-D LV_USE_SYSMON=0
-D LV_USE_PROFILER=0
-D LV_USE_PERF_MONITOR=0
-D LV_USE_MEM_MONITOR=0
-D LV_USE_LOG=0
-D LV_BUILD_TEST=0
-D USE_LOG_DEBUG
-D LOG_DEBUG_INC=\"DebugConfiguration.h\"
-D RADIOLIB_SPI_PARANOID=0
-D INPUTDRIVER_BUTTON_TYPE=0
-D HAS_SDCARD
-D HAS_SCREEN=0
-D HAS_TFT=1
-D USE_PIN_BUZZER=PIN_BUZZER
-D RAM_SIZE=5120
-D LGFX_DRIVER_TEMPLATE
-D LGFX_DRIVER=LGFX_GENERIC
-D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_GENERIC.h\"
-D LGFX_PIN_SCK=13
-D LGFX_PIN_MOSI=11
-D LGFX_PIN_MISO=10
-D LGFX_PIN_DC=42
-D LGFX_PIN_CS=12
-D LGFX_PIN_RST=-1
-D LGFX_PIN_BL=41
-D VIEW_320x240
-D USE_PACKET_API
${ft5x06.build_flags}
-D LGFX_SCREEN_WIDTH=240
-D LGFX_SCREEN_HEIGHT=320
-D DISPLAY_SIZE=320x240 ; landscape mode
-D LGFX_PANEL=ST7789
-D LGFX_ROTATION=1
-D LGFX_TOUCH_X_MIN=0
-D LGFX_TOUCH_X_MAX=239
-D LGFX_TOUCH_Y_MIN=0
-D LGFX_TOUCH_Y_MAX=319
-D LGFX_TOUCH_ROTATION=2
-D LGFX_CFG_HOST=SPI3_HOST
-D MAP_FULL_REDRAW=1
lib_deps =
${rak_wismeshtap_s3.lib_deps}
${device-ui_base.lib_deps}
@@ -0,0 +1,71 @@
#ifndef _VARIANT_RAK_WISMESHTAP_V2_H
#define _VARIANT_RAK_WISMESHTAP_V2_H
#define I2C_SDA 9
#define I2C_SCL 40
#define USE_SX1262
#define LORA_SCK 5
#define LORA_MISO 3
#define LORA_MOSI 6
#define LORA_CS 7
#define LORA_RESET 8
#ifdef USE_SX1262
#define SX126X_CS LORA_CS
#define SX126X_DIO1 47
#define SX126X_BUSY 48
#define SX126X_RESET LORA_RESET
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#endif
#define SX126X_POWER_EN (4)
#define PIN_POWER_EN PIN_3V3_EN
#define PIN_3V3_EN (14)
#define LED_GREEN 46
#define LED_BLUE 45
#define PIN_LED1 LED_GREEN
#define PIN_LED2 LED_BLUE
#define LED_CONN LED_BLUE
#define LED_PIN LED_GREEN
#define ledOff(pin) pinMode(pin, INPUT)
#define LED_STATE_ON 1 // State when LED is litted
#define HAS_GPS 1
#define GPS_TX_PIN 43
#define GPS_RX_PIN 44
#define SPI_MOSI (11)
#define SPI_SCK (13)
#define SPI_MISO (10)
#define SPI_CS (12)
#define HAS_BUTTON 1
#define BUTTON_PIN 0
#define CANNED_MESSAGE_MODULE_ENABLE 1
#define USE_VIRTUAL_KEYBOARD 1
#define BATTERY_PIN 1
#define ADC_CHANNEL ADC1_GPIO1_CHANNEL
#define ADC_MULTIPLIER 1.667
#define PIN_BUZZER 38
#define HAS_SDCARD 1
#define SDCARD_USE_SPI1 1
#define SDCARD_CS 2
#define SPI_FREQUENCY 40000000
#define SPI_READ_FREQUENCY 16000000
#define SD_SPI_FREQUENCY 50000000
#endif
@@ -6,7 +6,7 @@ platform_packages =
board = seeed-sensecap-indicator board = seeed-sensecap-indicator
board_check = true board_check = true
board_build.partitions = default_8MB.csv board_build.partitions = partition-table-8MB.csv
upload_protocol = esptool upload_protocol = esptool
build_flags = ${esp32_base.build_flags} build_flags = ${esp32_base.build_flags}
+1 -1
View File
@@ -26,7 +26,7 @@ lib_deps = ${esp32s3_base.lib_deps}
lewisxhe/SensorLib@0.3.1 lewisxhe/SensorLib@0.3.1
https://github.com/pschatzmann/arduino-audio-driver/archive/refs/tags/v0.1.3.zip https://github.com/pschatzmann/arduino-audio-driver/archive/refs/tags/v0.1.3.zip
https://github.com/mverch67/BQ27220/archive/07d92be846abd8a0258a50c23198dac0858b22ed.zip https://github.com/mverch67/BQ27220/archive/07d92be846abd8a0258a50c23198dac0858b22ed.zip
https://github.com/mverch67/RotaryEncoder https://github.com/mverch67/RotaryEncoder/archive/25a59d5745a6645536f921427d80b08e78f886d4.zip
[env:tlora-pager-tft] [env:tlora-pager-tft]
board_level = extra board_level = extra
+3 -1
View File
@@ -3,7 +3,7 @@
[env:unphone] [env:unphone]
extends = esp32s3_base extends = esp32s3_base
board = unphone board = unphone
board_build.partitions = default_8MB.csv board_build.partitions = partition-table-8MB.csv
upload_speed = 921600 upload_speed = 921600
monitor_speed = 115200 monitor_speed = 115200
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
@@ -20,6 +20,7 @@ build_flags =
-D UNPHONE_LORA=0 -D UNPHONE_LORA=0
-D UNPHONE_FACTORY_MODE=0 -D UNPHONE_FACTORY_MODE=0
-D USE_SX127x -D USE_SX127x
-D SDCARD_CS=43
build_src_filter = build_src_filter =
${esp32s3_base.build_src_filter} ${esp32s3_base.build_src_filter}
@@ -41,6 +42,7 @@ build_flags =
-D HAS_SCREEN=1 -D HAS_SCREEN=1
-D HAS_TFT=1 -D HAS_TFT=1
-D HAS_SDCARD -D HAS_SDCARD
-D SDCARD_CS=43
-D DISPLAY_SET_RESOLUTION -D DISPLAY_SET_RESOLUTION
-D RAM_SIZE=6144 -D RAM_SIZE=6144
-D LV_CACHE_DEF_SIZE=2097152 -D LV_CACHE_DEF_SIZE=2097152
-1
View File
@@ -52,7 +52,6 @@
#undef GPS_TX_PIN #undef GPS_TX_PIN
#define SD_SPI_FREQUENCY 25000000 #define SD_SPI_FREQUENCY 25000000
#define SDCARD_CS 43
#define LED_PIN 13 // the red part of the RGB LED #define LED_PIN 13 // the red part of the RGB LED
#define LED_STATE_ON 0 // State when LED is lit #define LED_STATE_ON 0 // State when LED is lit

Some files were not shown because too many files have changed in this diff Show More