mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-13 13:27:22 +00:00
Compare commits
38 Commits
baseui_nod
...
InkHUD-Imp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db0ca0324e | ||
|
|
750f695bbd | ||
|
|
2cbb8040f3 | ||
|
|
ef128a7883 | ||
|
|
d55bf66f25 | ||
|
|
1885a2beac | ||
|
|
d5ef68314b | ||
|
|
3baba4b1a1 | ||
|
|
791fb86c7c | ||
|
|
c1c5d36e86 | ||
|
|
050371adc5 | ||
|
|
f409645ad3 | ||
|
|
25d7db65ea | ||
|
|
e0ceaaff38 | ||
|
|
6431c76aac | ||
|
|
ac0b3613ec | ||
|
|
9ad7d39051 | ||
|
|
b3e6731c85 | ||
|
|
ef36a5a24d | ||
|
|
66d9c430d8 | ||
|
|
ac05337e42 | ||
|
|
1d4e295471 | ||
|
|
c761444bee | ||
|
|
929aa5c968 | ||
|
|
cc6265e9b1 | ||
|
|
958e1f73ef | ||
|
|
96e82f1ec1 | ||
|
|
83ec37113d | ||
|
|
5acf72243d | ||
|
|
bd18a171d4 | ||
|
|
6e05c554b8 | ||
|
|
5f9a6a38e6 | ||
|
|
a332ca978b | ||
|
|
7b4315421b | ||
|
|
60389e84e6 | ||
|
|
cd0843c7db | ||
|
|
d9dab0cd6c | ||
|
|
87114f4052 |
79
.github/workflows/build_firmware.yml
vendored
79
.github/workflows/build_firmware.yml
vendored
@@ -29,6 +29,23 @@ jobs:
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
- name: Set OTA firmware source and target
|
||||
if: startsWith(inputs.platform, 'esp32')
|
||||
id: ota_dir
|
||||
env:
|
||||
PIO_PLATFORM: ${{ inputs.platform }}
|
||||
run: |
|
||||
if [ "$PIO_PLATFORM" = "esp32s3" ]; then
|
||||
echo "src=firmware-s3.bin" >> $GITHUB_OUTPUT
|
||||
echo "tgt=release/bleota-s3.bin" >> $GITHUB_OUTPUT
|
||||
elif [ "$PIO_PLATFORM" = "esp32c3" ] || [ "$PIO_PLATFORM" = "esp32c6" ]; then
|
||||
echo "src=firmware-c3.bin" >> $GITHUB_OUTPUT
|
||||
echo "tgt=release/bleota-c3.bin" >> $GITHUB_OUTPUT
|
||||
elif [ "$PIO_PLATFORM" = "esp32" ]; then
|
||||
echo "src=firmware.bin" >> $GITHUB_OUTPUT
|
||||
echo "tgt=release/bleota.bin" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Build ${{ inputs.platform }}
|
||||
id: build
|
||||
uses: meshtastic/gh-action-firmware@main
|
||||
@@ -36,66 +53,8 @@ jobs:
|
||||
pio_platform: ${{ inputs.platform }}
|
||||
pio_env: ${{ inputs.pio_env }}
|
||||
pio_target: build
|
||||
|
||||
- name: ESP32 - Download Unified OTA firmware
|
||||
# Currently only esp32 and esp32s3 use the unified ota
|
||||
if: inputs.platform == 'esp32' || inputs.platform == 'esp32s3'
|
||||
id: dl-ota-unified
|
||||
env:
|
||||
PIO_PLATFORM: ${{ inputs.platform }}
|
||||
PIO_ENV: ${{ inputs.pio_env }}
|
||||
OTA_URL: https://github.com/meshtastic/esp32-unified-ota/releases/latest/download/mt-${{ inputs.platform }}-ota.bin
|
||||
working-directory: release
|
||||
run: |
|
||||
curl -L -o "mt-$PIO_PLATFORM-ota.bin" $OTA_URL
|
||||
|
||||
- name: ESP32-C* - Download BLE-Only OTA firmware
|
||||
if: inputs.platform == 'esp32c3' || inputs.platform == 'esp32c6'
|
||||
id: dl-ota-ble
|
||||
env:
|
||||
PIO_ENV: ${{ inputs.pio_env }}
|
||||
OTA_URL: https://github.com/meshtastic/firmware-ota/releases/latest/download/firmware-c3.bin
|
||||
working-directory: release
|
||||
run: |
|
||||
curl -L -o bleota-c3.bin $OTA_URL
|
||||
|
||||
- name: Update manifest with OTA file
|
||||
if: inputs.platform == 'esp32' || inputs.platform == 'esp32s3' || inputs.platform == 'esp32c3' || inputs.platform == 'esp32c6'
|
||||
working-directory: release
|
||||
env:
|
||||
PIO_PLATFORM: ${{ inputs.platform }}
|
||||
run: |
|
||||
# Determine OTA filename based on platform
|
||||
if [[ "$PIO_PLATFORM" == "esp32" || "$PIO_PLATFORM" == "esp32s3" ]]; then
|
||||
OTA_FILE="mt-${PIO_PLATFORM}-ota.bin"
|
||||
else
|
||||
OTA_FILE="bleota-c3.bin"
|
||||
fi
|
||||
|
||||
# Check if OTA file exists
|
||||
if [[ ! -f "$OTA_FILE" ]]; then
|
||||
echo "OTA file $OTA_FILE not found, skipping manifest update"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Calculate MD5 and size
|
||||
if command -v md5sum &> /dev/null; then
|
||||
OTA_MD5=$(md5sum "$OTA_FILE" | cut -d' ' -f1)
|
||||
else
|
||||
OTA_MD5=$(md5 -q "$OTA_FILE")
|
||||
fi
|
||||
OTA_SIZE=$(stat -f%z "$OTA_FILE" 2>/dev/null || stat -c%s "$OTA_FILE")
|
||||
|
||||
# Find and update manifest file
|
||||
for manifest in firmware-*.mt.json; do
|
||||
if [[ -f "$manifest" ]]; then
|
||||
echo "Updating $manifest with $OTA_FILE (md5: $OTA_MD5, size: $OTA_SIZE)"
|
||||
# Add OTA entry to files array if not already present
|
||||
jq --arg name "$OTA_FILE" --arg md5 "$OTA_MD5" --argjson bytes "$OTA_SIZE" \
|
||||
'if .files | map(select(.name == $name)) | length == 0 then .files += [{"name": $name, "md5": $md5, "bytes": $bytes}] else . end' \
|
||||
"$manifest" > "${manifest}.tmp" && mv "${manifest}.tmp" "$manifest"
|
||||
fi
|
||||
done
|
||||
ota_firmware_source: ${{ steps.ota_dir.outputs.src || '' }}
|
||||
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
|
||||
|
||||
- name: Job summary
|
||||
env:
|
||||
|
||||
31
.github/workflows/main_matrix.yml
vendored
31
.github/workflows/main_matrix.yml
vendored
@@ -201,7 +201,6 @@ jobs:
|
||||
./device-*.bat
|
||||
./littlefs-*.bin
|
||||
./bleota*bin
|
||||
./mt-*-ota.bin
|
||||
./Meshtastic_nRF52_factory_erase*.uf2
|
||||
retention-days: 30
|
||||
|
||||
@@ -294,24 +293,6 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
- name: Generate release notes
|
||||
id: release_notes
|
||||
run: |
|
||||
chmod +x ./bin/generate_release_notes.py
|
||||
NOTES=$(./bin/generate_release_notes.py ${{ needs.version.outputs.long }})
|
||||
echo "notes<<EOF" >> $GITHUB_OUTPUT
|
||||
echo "$NOTES" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Create release
|
||||
uses: softprops/action-gh-release@v2
|
||||
@@ -321,7 +302,8 @@ jobs:
|
||||
prerelease: true
|
||||
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
|
||||
tag_name: v${{ needs.version.outputs.long }}
|
||||
body: ${{ steps.release_notes.outputs.notes }}
|
||||
body: |
|
||||
Autogenerated by github action, developer should edit as required before publishing...
|
||||
|
||||
- name: Download source deb
|
||||
uses: actions/download-artifact@v7
|
||||
@@ -445,8 +427,6 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
@@ -466,13 +446,6 @@ jobs:
|
||||
pattern: manifest-${{ needs.version.outputs.long }}
|
||||
path: ./publish
|
||||
|
||||
- name: Generate release notes
|
||||
run: |
|
||||
chmod +x ./bin/generate_release_notes.py
|
||||
./bin/generate_release_notes.py ${{ needs.version.outputs.long }} > ./publish/release_notes.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Publish firmware to meshtastic.github.io
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
env:
|
||||
|
||||
31
.github/workflows/release_channels.yml
vendored
31
.github/workflows/release_channels.yml
vendored
@@ -48,37 +48,6 @@ jobs:
|
||||
${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }}
|
||||
secrets: inherit
|
||||
|
||||
publish-release-notes:
|
||||
if: github.event.action == 'published'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get release version
|
||||
id: version
|
||||
run: |
|
||||
# Extract version from tag (e.g., v2.7.15.567b8ea -> 2.7.15.567b8ea)
|
||||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get release notes
|
||||
run: |
|
||||
mkdir -p ./publish
|
||||
gh release view ${{ github.event.release.tag_name }} --json body --jq '.body' > ./publish/release_notes.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Publish release notes to meshtastic.github.io
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
deploy_key: ${{ secrets.DIST_PAGES_DEPLOY_KEY }}
|
||||
external_repository: meshtastic/meshtastic.github.io
|
||||
publish_branch: master
|
||||
publish_dir: ./publish
|
||||
destination_dir: firmware-${{ steps.version.outputs.version }}
|
||||
user_name: github-actions[bot]
|
||||
user_email: github-actions[bot]@users.noreply.github.com
|
||||
commit_message: Release notes for ${{ steps.version.outputs.version }}
|
||||
enable_jekyll: true
|
||||
|
||||
# Create a PR to bump version when a release is Published
|
||||
bump-version:
|
||||
if: github.event.action == 'published'
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -48,5 +48,5 @@ arduino-lib-builder*
|
||||
dependencies.lock
|
||||
idf_component.yml
|
||||
CMakeLists.txt
|
||||
/sdkconfig.*
|
||||
sdkconfig.*
|
||||
.dummy/*
|
||||
|
||||
@@ -9,7 +9,7 @@ plugins:
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.497
|
||||
- renovate@42.78.2
|
||||
- renovate@42.75.0
|
||||
- prettier@3.7.4
|
||||
- trufflehog@3.92.4
|
||||
- yamllint@1.37.1
|
||||
|
||||
@@ -105,8 +105,6 @@ Lora:
|
||||
|
||||
GPS:
|
||||
# SerialPath: /dev/ttyS0
|
||||
# ExtraPins:
|
||||
# - 22
|
||||
|
||||
### Specify I2C device, or leave blank for none
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
Lora:
|
||||
Module: sx1262
|
||||
CS: 0
|
||||
IRQ: 6
|
||||
Reset: 1
|
||||
Busy: 4
|
||||
RXen: 2
|
||||
DIO2_AS_RF_SWITCH: true
|
||||
spidev: ch341
|
||||
USB_PID: 0x5512
|
||||
USB_VID: 0x1A86
|
||||
DIO3_TCXO_VOLTAGE: true
|
||||
# USB_Serialnum: 12345678
|
||||
SX126X_MAX_POWER: 30
|
||||
# Reduce output power to improve EMI
|
||||
@@ -1,15 +0,0 @@
|
||||
Lora:
|
||||
Module: sx1268
|
||||
CS: 0
|
||||
IRQ: 6
|
||||
Reset: 1
|
||||
Busy: 4
|
||||
RXen: 2
|
||||
DIO2_AS_RF_SWITCH: true
|
||||
spidev: ch341
|
||||
USB_PID: 0x5512
|
||||
USB_VID: 0x1A86
|
||||
DIO3_TCXO_VOLTAGE: true
|
||||
# USB_Serialnum: 12345678
|
||||
SX126X_MAX_POWER: 30
|
||||
# Reduce output power to improve EMI
|
||||
@@ -1,355 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate release notes from merged PRs on develop and master branches.
|
||||
Categorizes PRs into Enhancements and Bug Fixes/Maintenance sections.
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import re
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def get_last_release_tag():
|
||||
"""Get the most recent release tag."""
|
||||
result = subprocess.run(
|
||||
["git", "describe", "--tags", "--abbrev=0"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def get_tag_date(tag):
|
||||
"""Get the commit date (ISO 8601) of the tag."""
|
||||
result = subprocess.run(
|
||||
["git", "show", "-s", "--format=%cI", tag],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def get_merged_prs_since_tag(tag, branch):
|
||||
"""Get all merged PRs since the given tag on the specified branch."""
|
||||
# Get commits since tag on the branch - look for PR numbers in parentheses
|
||||
result = subprocess.run(
|
||||
[
|
||||
"git",
|
||||
"log",
|
||||
f"{tag}..origin/{branch}",
|
||||
"--oneline",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
prs = []
|
||||
seen_pr_numbers = set()
|
||||
|
||||
for line in result.stdout.strip().split("\n"):
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# Extract PR number from commit message - format: "Title (#1234)"
|
||||
pr_match = re.search(r"\(#(\d+)\)", line)
|
||||
if pr_match:
|
||||
pr_number = pr_match.group(1)
|
||||
if pr_number not in seen_pr_numbers:
|
||||
seen_pr_numbers.add(pr_number)
|
||||
prs.append(pr_number)
|
||||
|
||||
return prs
|
||||
|
||||
|
||||
def get_pr_details(pr_number):
|
||||
"""Get PR details from GitHub API via gh CLI."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[
|
||||
"gh",
|
||||
"pr",
|
||||
"view",
|
||||
pr_number,
|
||||
"--json",
|
||||
"title,author,labels,url",
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
return json.loads(result.stdout)
|
||||
except subprocess.CalledProcessError:
|
||||
return None
|
||||
|
||||
|
||||
def should_exclude_pr(pr_details):
|
||||
"""Check if PR should be excluded from release notes."""
|
||||
if not pr_details:
|
||||
return True
|
||||
|
||||
title = pr_details.get("title", "").lower()
|
||||
|
||||
# Exclude trunk update PRs
|
||||
if "upgrade trunk" in title or "update trunk" in title or "trunk update" in title:
|
||||
return True
|
||||
|
||||
# Exclude protobuf update PRs
|
||||
if "update protobufs" in title or "update protobuf" in title:
|
||||
return True
|
||||
|
||||
# Exclude automated version bump PRs
|
||||
if "bump release version" in title or "bump version" in title:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def is_dependency_update(pr_details):
|
||||
"""Check if PR is a dependency/chore update."""
|
||||
if not pr_details:
|
||||
return False
|
||||
|
||||
title = pr_details.get("title", "").lower()
|
||||
author = pr_details.get("author", {}).get("login", "").lower()
|
||||
labels = [label.get("name", "").lower() for label in pr_details.get("labels", [])]
|
||||
|
||||
# Check for renovate or dependabot authors
|
||||
if "renovate" in author or "dependabot" in author:
|
||||
return True
|
||||
|
||||
# Check for chore(deps) pattern
|
||||
if re.match(r"^chore\(deps\):", title):
|
||||
return True
|
||||
|
||||
# Check for digest update patterns
|
||||
if re.match(r".*digest to [a-f0-9]+", title, re.IGNORECASE):
|
||||
return True
|
||||
|
||||
# Check for dependency-related labels
|
||||
dependency_labels = ["dependencies", "deps", "renovate"]
|
||||
if any(dep in label for label in labels for dep in dependency_labels):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def is_enhancement(pr_details):
|
||||
"""Determine if PR is an enhancement based on labels and title."""
|
||||
labels = [label.get("name", "").lower() for label in pr_details.get("labels", [])]
|
||||
|
||||
# Check labels first
|
||||
enhancement_labels = ["enhancement", "feature", "feat", "new feature"]
|
||||
for label in labels:
|
||||
if any(enh in label for enh in enhancement_labels):
|
||||
return True
|
||||
|
||||
# Check title prefixes
|
||||
title = pr_details.get("title", "")
|
||||
enhancement_prefixes = ["feat:", "feature:", "add:"]
|
||||
title_lower = title.lower()
|
||||
for prefix in enhancement_prefixes:
|
||||
if title_lower.startswith(prefix) or f" {prefix}" in title_lower:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def clean_title(title):
|
||||
"""Clean up PR title for release notes."""
|
||||
# Remove common prefixes
|
||||
prefixes_to_remove = [
|
||||
r"^fix:\s*",
|
||||
r"^feat:\s*",
|
||||
r"^feature:\s*",
|
||||
r"^bug:\s*",
|
||||
r"^bugfix:\s*",
|
||||
r"^chore:\s*",
|
||||
r"^chore\([^)]+\):\s*",
|
||||
r"^refactor:\s*",
|
||||
r"^docs:\s*",
|
||||
r"^ci:\s*",
|
||||
r"^build:\s*",
|
||||
r"^perf:\s*",
|
||||
r"^style:\s*",
|
||||
r"^test:\s*",
|
||||
]
|
||||
|
||||
cleaned = title
|
||||
for prefix in prefixes_to_remove:
|
||||
cleaned = re.sub(prefix, "", cleaned, flags=re.IGNORECASE)
|
||||
|
||||
# Ensure first letter is capitalized
|
||||
if cleaned:
|
||||
cleaned = cleaned[0].upper() + cleaned[1:]
|
||||
|
||||
return cleaned.strip()
|
||||
|
||||
|
||||
def format_pr_line(pr_details):
|
||||
"""Format a PR as a markdown bullet point."""
|
||||
title = clean_title(pr_details.get("title", "Unknown"))
|
||||
author = pr_details.get("author", {}).get("login", "unknown")
|
||||
url = pr_details.get("url", "")
|
||||
|
||||
return f"- {title} by @{author} in {url}"
|
||||
|
||||
|
||||
def get_new_contributors(pr_details_list, tag, repo="meshtastic/firmware"):
|
||||
"""Find contributors who made their first merged PR before this release.
|
||||
|
||||
GitHub usernames do not necessarily match git commit authors, so we use the
|
||||
GitHub search API via `gh` to see if the user has any merged PRs before the
|
||||
tag date. This mirrors how GitHub's "Generate release notes" feature works.
|
||||
"""
|
||||
|
||||
bot_authors = {"github-actions", "renovate", "dependabot", "app/renovate", "app/github-actions", "app/dependabot"}
|
||||
|
||||
new_contributors = []
|
||||
seen_authors = set()
|
||||
|
||||
try:
|
||||
tag_date = get_tag_date(tag)
|
||||
except subprocess.CalledProcessError:
|
||||
print(f"Warning: Could not determine tag date for {tag}; skipping new contributor detection", file=sys.stderr)
|
||||
return []
|
||||
|
||||
for pr in pr_details_list:
|
||||
author = pr.get("author", {}).get("login", "")
|
||||
if not author or author in seen_authors:
|
||||
continue
|
||||
|
||||
# Skip bots
|
||||
if author.lower() in bot_authors or author.startswith("app/"):
|
||||
continue
|
||||
|
||||
seen_authors.add(author)
|
||||
|
||||
try:
|
||||
# Search for merged PRs by this author created before the tag date
|
||||
search_query = f"is:pr author:{author} repo:{repo} closed:<=\"{tag_date}\""
|
||||
search = subprocess.run(
|
||||
[
|
||||
"gh",
|
||||
"search",
|
||||
"issues",
|
||||
"--json",
|
||||
"number,mergedAt,createdAt",
|
||||
"--state",
|
||||
"closed",
|
||||
"--limit",
|
||||
"200",
|
||||
search_query,
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
if search.returncode != 0:
|
||||
# If gh fails, be conservative and skip adding to new contributors
|
||||
print(f"Warning: gh search failed for author {author}: {search.stderr.strip()}", file=sys.stderr)
|
||||
continue
|
||||
|
||||
results = json.loads(search.stdout or "[]")
|
||||
# If any merged PR exists before or on tag date, not a new contributor
|
||||
had_prior_pr = any(item.get("mergedAt") for item in results)
|
||||
|
||||
if not had_prior_pr:
|
||||
new_contributors.append((author, pr.get("url", "")))
|
||||
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not check contributor history for {author}: {e}", file=sys.stderr)
|
||||
continue
|
||||
|
||||
return new_contributors
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: generate_release_notes.py <new_version>", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
new_version = sys.argv[1]
|
||||
|
||||
# Get last release tag
|
||||
try:
|
||||
last_tag = get_last_release_tag()
|
||||
except subprocess.CalledProcessError:
|
||||
print("Error: Could not find last release tag", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Collect PRs from both branches
|
||||
all_pr_numbers = set()
|
||||
|
||||
for branch in ["develop", "master"]:
|
||||
try:
|
||||
prs = get_merged_prs_since_tag(last_tag, branch)
|
||||
all_pr_numbers.update(prs)
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not get PRs from {branch}: {e}", file=sys.stderr)
|
||||
|
||||
# Get details for all PRs
|
||||
enhancements = []
|
||||
bug_fixes = []
|
||||
dependencies = []
|
||||
all_pr_details = []
|
||||
|
||||
for pr_number in sorted(all_pr_numbers, key=int):
|
||||
details = get_pr_details(pr_number)
|
||||
if details and not should_exclude_pr(details):
|
||||
all_pr_details.append(details)
|
||||
if is_dependency_update(details):
|
||||
dependencies.append(details)
|
||||
elif is_enhancement(details):
|
||||
enhancements.append(details)
|
||||
else:
|
||||
bug_fixes.append(details)
|
||||
|
||||
# Generate release notes
|
||||
output = []
|
||||
|
||||
if enhancements:
|
||||
output.append("## 🚀 Enhancements\n")
|
||||
for pr in enhancements:
|
||||
output.append(format_pr_line(pr))
|
||||
output.append("")
|
||||
|
||||
if bug_fixes:
|
||||
output.append("## 🐛 Bug fixes and maintenance\n")
|
||||
for pr in bug_fixes:
|
||||
output.append(format_pr_line(pr))
|
||||
output.append("")
|
||||
|
||||
if dependencies:
|
||||
output.append("## ⚙️ Dependencies\n")
|
||||
for pr in dependencies:
|
||||
output.append(format_pr_line(pr))
|
||||
output.append("")
|
||||
|
||||
# Find new contributors (GitHub-accurate check using merged PRs before tag date)
|
||||
new_contributors = get_new_contributors(all_pr_details, last_tag)
|
||||
if new_contributors:
|
||||
output.append("## New Contributors\n")
|
||||
for author, url in new_contributors:
|
||||
# Find first PR URL for this contributor
|
||||
first_pr_url = url
|
||||
for pr in all_pr_details:
|
||||
if pr.get("author", {}).get("login") == author:
|
||||
first_pr_url = pr.get("url", url)
|
||||
break
|
||||
output.append(f"- @{author} made their first contribution in {first_pr_url}")
|
||||
output.append("")
|
||||
|
||||
# Add full changelog link
|
||||
output.append(
|
||||
f"**Full Changelog**: https://github.com/meshtastic/firmware/compare/{last_tag}...v{new_version}"
|
||||
)
|
||||
|
||||
print("\n".join(output))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -58,7 +58,6 @@ def manifest_gather(source, target, env):
|
||||
manifest_ran = True
|
||||
out = []
|
||||
board_platform = env.BoardConfig().get("platform")
|
||||
board_mcu = env.BoardConfig().get("build.mcu").lower()
|
||||
needs_ota_suffix = board_platform == "nordicnrf52"
|
||||
check_paths = [
|
||||
progname,
|
||||
@@ -70,9 +69,7 @@ def manifest_gather(source, target, env):
|
||||
f"{progname}.uf2",
|
||||
f"{progname}.factory.uf2",
|
||||
f"{progname}.zip",
|
||||
lfsbin,
|
||||
f"mt-{board_mcu}-ota.bin",
|
||||
"bleota-c3.bin"
|
||||
lfsbin
|
||||
]
|
||||
for p in check_paths:
|
||||
f = env.File(env.subst(f"$BUILD_DIR/{p}"))
|
||||
|
||||
@@ -54,7 +54,6 @@ build_flags = -Wno-missing-field-initializers
|
||||
-DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1
|
||||
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
|
||||
-DMESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE=1
|
||||
-DMESHTASTIC_EXCLUDE_POWERMON=1
|
||||
-D MAX_THREADS=40 ; As we've split modules, we have more threads to manage
|
||||
#-DBUILD_EPOCH=$UNIX_TIME ; set in platformio-custom.py now
|
||||
#-D OLED_PL=1
|
||||
@@ -114,12 +113,13 @@ lib_deps =
|
||||
[radiolib_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
|
||||
jgromes/RadioLib@7.5.0
|
||||
# jgromes/RadioLib@7.4.0
|
||||
https://github.com/jgromes/RadioLib/archive/536c7267362e2c1345be7054ba45e503252975ff.zip
|
||||
|
||||
[device-ui_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||
https://github.com/meshtastic/device-ui/archive/12f8cddc1e2908e1988da21e3500c695668e8d92.zip
|
||||
https://github.com/meshtastic/device-ui/archive/272defcb35651461830ebfd1b39c9167c8f49317.zip
|
||||
|
||||
; Common libs for environmental measurements in telemetry module
|
||||
[environmental_base]
|
||||
|
||||
Submodule protobufs updated: 61219de748...1a63a3d0d2
@@ -41,8 +41,7 @@ extern "C" void logLegacy(const char *level, const char *fmt, ...)
|
||||
}
|
||||
|
||||
#if HAS_NETWORKING
|
||||
namespace meshtastic
|
||||
{
|
||||
|
||||
Syslog::Syslog(UDP &client)
|
||||
{
|
||||
this->_client = &client;
|
||||
@@ -196,6 +195,4 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace meshtastic
|
||||
|
||||
#endif
|
||||
|
||||
@@ -162,8 +162,6 @@ extern "C" void logLegacy(const char *level, const char *fmt, ...);
|
||||
|
||||
#if HAS_NETWORKING
|
||||
|
||||
namespace meshtastic
|
||||
{
|
||||
class Syslog
|
||||
{
|
||||
private:
|
||||
@@ -197,6 +195,4 @@ class Syslog
|
||||
bool vlogf(uint16_t pri, const char *appName, const char *fmt, va_list args) __attribute__((format(printf, 3, 0)));
|
||||
};
|
||||
|
||||
}; // namespace meshtastic
|
||||
|
||||
#endif // HAS_NETWORKING
|
||||
#endif // HAS_NETWORKING
|
||||
@@ -13,11 +13,6 @@
|
||||
#define MESSAGE_TEXT_POOL_SIZE (MAX_MESSAGES_SAVED * MAX_MESSAGE_SIZE)
|
||||
#endif
|
||||
|
||||
// Default autosave interval 2 hours, override per device later with -DMESSAGE_AUTOSAVE_INTERVAL_SEC=300 (etc)
|
||||
#ifndef MESSAGE_AUTOSAVE_INTERVAL_SEC
|
||||
#define MESSAGE_AUTOSAVE_INTERVAL_SEC (2 * 60 * 60)
|
||||
#endif
|
||||
|
||||
// Global message text pool and state
|
||||
static char *g_messagePool = nullptr;
|
||||
static size_t g_poolWritePos = 0;
|
||||
@@ -107,60 +102,6 @@ void MessageStore::addLiveMessage(const StoredMessage &msg)
|
||||
pushWithLimit(liveMessages, msg);
|
||||
}
|
||||
|
||||
#if ENABLE_MESSAGE_PERSISTENCE
|
||||
static bool g_messageStoreHasUnsavedChanges = false;
|
||||
static uint32_t g_lastAutoSaveMs = 0; // last time we actually saved
|
||||
|
||||
static inline uint32_t autosaveIntervalMs()
|
||||
{
|
||||
uint32_t sec = (uint32_t)MESSAGE_AUTOSAVE_INTERVAL_SEC;
|
||||
if (sec < 60)
|
||||
sec = 60;
|
||||
return sec * 1000UL;
|
||||
}
|
||||
|
||||
static inline bool reachedMs(uint32_t now, uint32_t target)
|
||||
{
|
||||
return (int32_t)(now - target) >= 0;
|
||||
}
|
||||
|
||||
// Mark new messages in RAM that need to be saved later
|
||||
static inline void markMessageStoreUnsaved()
|
||||
{
|
||||
g_messageStoreHasUnsavedChanges = true;
|
||||
|
||||
if (g_lastAutoSaveMs == 0) {
|
||||
g_lastAutoSaveMs = millis();
|
||||
}
|
||||
}
|
||||
|
||||
// Called periodically from the main loop in main.cpp
|
||||
static inline void autosaveTick(MessageStore *store)
|
||||
{
|
||||
if (!store)
|
||||
return;
|
||||
|
||||
uint32_t now = millis();
|
||||
|
||||
if (g_lastAutoSaveMs == 0) {
|
||||
g_lastAutoSaveMs = now;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reachedMs(now, g_lastAutoSaveMs + autosaveIntervalMs()))
|
||||
return;
|
||||
|
||||
// Autosave interval reached, only save if there are unsaved messages.
|
||||
if (g_messageStoreHasUnsavedChanges) {
|
||||
LOG_INFO("Autosaving MessageStore to flash");
|
||||
store->saveToFlash();
|
||||
} else {
|
||||
LOG_INFO("Autosave skipped, no changes to save");
|
||||
g_lastAutoSaveMs = now;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Add from incoming/outgoing packet
|
||||
const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &packet)
|
||||
{
|
||||
@@ -190,11 +131,6 @@ const StoredMessage &MessageStore::addFromPacket(const meshtastic_MeshPacket &pa
|
||||
}
|
||||
|
||||
addLiveMessage(sm);
|
||||
|
||||
#if ENABLE_MESSAGE_PERSISTENCE
|
||||
markMessageStoreUnsaved();
|
||||
#endif
|
||||
|
||||
return liveMessages.back();
|
||||
}
|
||||
|
||||
@@ -219,10 +155,6 @@ void MessageStore::addFromString(uint32_t sender, uint8_t channelIndex, const st
|
||||
sm.ackStatus = AckStatus::NONE;
|
||||
|
||||
addLiveMessage(sm);
|
||||
|
||||
#if ENABLE_MESSAGE_PERSISTENCE
|
||||
markMessageStoreUnsaved();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLE_MESSAGE_PERSISTENCE
|
||||
@@ -307,10 +239,6 @@ void MessageStore::saveToFlash()
|
||||
|
||||
f.close();
|
||||
#endif
|
||||
|
||||
// Reset autosave state after any save
|
||||
g_messageStoreHasUnsavedChanges = false;
|
||||
g_lastAutoSaveMs = millis();
|
||||
}
|
||||
|
||||
void MessageStore::loadFromFlash()
|
||||
@@ -342,9 +270,6 @@ void MessageStore::loadFromFlash()
|
||||
|
||||
f.close();
|
||||
#endif
|
||||
// Loading messages does not trigger an autosave
|
||||
g_messageStoreHasUnsavedChanges = false;
|
||||
g_lastAutoSaveMs = millis();
|
||||
}
|
||||
|
||||
#else
|
||||
@@ -365,11 +290,6 @@ void MessageStore::clearAllMessages()
|
||||
f.write(&count, 1); // write "0 messages"
|
||||
f.close();
|
||||
#endif
|
||||
|
||||
#if ENABLE_MESSAGE_PERSISTENCE
|
||||
g_messageStoreHasUnsavedChanges = false;
|
||||
g_lastAutoSaveMs = millis();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Internal helper: erase first or last message matching a predicate
|
||||
@@ -501,14 +421,6 @@ uint16_t MessageStore::storeText(const char *src, size_t len)
|
||||
return storeTextInPool(src, len);
|
||||
}
|
||||
|
||||
#if ENABLE_MESSAGE_PERSISTENCE
|
||||
void messageStoreAutosaveTick()
|
||||
{
|
||||
// Called from the main loop to check autosave timing
|
||||
autosaveTick(&messageStore);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Global definition
|
||||
MessageStore messageStore("default");
|
||||
#endif
|
||||
#endif
|
||||
@@ -125,11 +125,6 @@ class MessageStore
|
||||
std::string filename; // Flash filename for persistence
|
||||
};
|
||||
|
||||
#if ENABLE_MESSAGE_PERSISTENCE
|
||||
// Called periodically from main loop to trigger time based autosave
|
||||
void messageStoreAutosaveTick();
|
||||
#endif
|
||||
|
||||
// Global instance (defined in MessageStore.cpp)
|
||||
extern MessageStore messageStore;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#endif
|
||||
|
||||
#if HAS_NETWORKING
|
||||
extern meshtastic::Syslog syslog;
|
||||
extern Syslog syslog;
|
||||
#endif
|
||||
void RedirectablePrint::rpInit()
|
||||
{
|
||||
|
||||
@@ -59,7 +59,6 @@ BannerOverlayOptions createStaticBannerOptions(const char *message, const MenuOp
|
||||
} // namespace
|
||||
|
||||
menuHandler::screenMenus menuHandler::menuQueue = menu_none;
|
||||
uint32_t menuHandler::pickedNodeNum = 0;
|
||||
bool test_enabled = false;
|
||||
uint8_t test_count = 0;
|
||||
|
||||
@@ -450,7 +449,7 @@ void menuHandler::clockMenu()
|
||||
}
|
||||
void menuHandler::messageResponseMenu()
|
||||
{
|
||||
enum optionsNumbers { Back = 0, ViewMode, DeleteMenu, ReplyMenu, MuteChannel, Aloud, enumEnd };
|
||||
enum optionsNumbers { Back = 0, ViewMode, DeleteAll, DeleteOldest, ReplyMenu, MuteChannel, Aloud, enumEnd };
|
||||
|
||||
static const char *optionsArray[enumEnd];
|
||||
static int optionsEnumArray[enumEnd];
|
||||
@@ -480,7 +479,7 @@ void menuHandler::messageResponseMenu()
|
||||
|
||||
// Delete submenu
|
||||
optionsArray[options] = "Delete";
|
||||
optionsEnumArray[options++] = DeleteMenu;
|
||||
optionsEnumArray[options++] = 900;
|
||||
|
||||
#ifdef HAS_I2S
|
||||
optionsArray[options] = "Read Aloud";
|
||||
@@ -521,10 +520,34 @@ void menuHandler::messageResponseMenu()
|
||||
nodeDB->saveToDisk();
|
||||
}
|
||||
|
||||
} else if (selected == DeleteMenu) {
|
||||
// Delete submenu
|
||||
} else if (selected == 900) {
|
||||
menuHandler::menuQueue = menuHandler::delete_messages_menu;
|
||||
screen->runNow();
|
||||
|
||||
// Delete oldest FIRST (only change)
|
||||
} else if (selected == DeleteOldest) {
|
||||
auto mode = graphics::MessageRenderer::getThreadMode();
|
||||
int ch = graphics::MessageRenderer::getThreadChannel();
|
||||
uint32_t peer = graphics::MessageRenderer::getThreadPeer();
|
||||
|
||||
if (mode == graphics::MessageRenderer::ThreadMode::ALL) {
|
||||
// Global oldest
|
||||
messageStore.deleteOldestMessage();
|
||||
} else if (mode == graphics::MessageRenderer::ThreadMode::CHANNEL) {
|
||||
// Oldest in current channel
|
||||
messageStore.deleteOldestMessageInChannel(ch);
|
||||
} else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
|
||||
// Oldest in current DM
|
||||
messageStore.deleteOldestMessageWithPeer(peer);
|
||||
}
|
||||
|
||||
// Delete all messages
|
||||
} else if (selected == DeleteAll) {
|
||||
messageStore.clearAllMessages();
|
||||
graphics::MessageRenderer::clearThreadRegistries();
|
||||
graphics::MessageRenderer::clearMessageCache();
|
||||
|
||||
#ifdef HAS_I2S
|
||||
} else if (selected == Aloud) {
|
||||
const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
|
||||
@@ -693,6 +716,7 @@ void menuHandler::deleteMessagesMenu()
|
||||
} else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
|
||||
messageStore.deleteOldestMessageWithPeer(peer);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -705,6 +729,7 @@ void menuHandler::deleteMessagesMenu()
|
||||
} else if (mode == graphics::MessageRenderer::ThreadMode::DIRECT) {
|
||||
messageStore.deleteAllMessagesWithPeer(peer);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -1214,13 +1239,20 @@ void menuHandler::positionBaseMenu()
|
||||
|
||||
void menuHandler::nodeListMenu()
|
||||
{
|
||||
enum optionsNumbers { Back, NodePicker, TraceRoute, Verify, Reset, NodeNameLength, enumEnd };
|
||||
enum optionsNumbers { Back, Favorite, TraceRoute, Verify, Reset, NodeNameLength, enumEnd };
|
||||
static const char *optionsArray[enumEnd] = {"Back"};
|
||||
static int optionsEnumArray[enumEnd] = {Back};
|
||||
int options = 1;
|
||||
|
||||
optionsArray[options] = "Node Actions / Settings";
|
||||
optionsEnumArray[options++] = NodePicker;
|
||||
optionsArray[options] = "Add Favorite";
|
||||
optionsEnumArray[options++] = Favorite;
|
||||
optionsArray[options] = "Trace Route";
|
||||
optionsEnumArray[options++] = TraceRoute;
|
||||
|
||||
if (currentResolution != ScreenResolution::UltraLow) {
|
||||
optionsArray[options] = "Key Verification";
|
||||
optionsEnumArray[options++] = Verify;
|
||||
}
|
||||
|
||||
if (currentResolution != ScreenResolution::UltraLow) {
|
||||
optionsArray[options] = "Show Long/Short Name";
|
||||
@@ -1235,12 +1267,18 @@ void menuHandler::nodeListMenu()
|
||||
bannerOptions.optionsCount = options;
|
||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == NodePicker) {
|
||||
menuQueue = NodePicker_menu;
|
||||
if (selected == Favorite) {
|
||||
menuQueue = add_favorite;
|
||||
screen->runNow();
|
||||
} else if (selected == Verify) {
|
||||
menuQueue = key_verification_init;
|
||||
screen->runNow();
|
||||
} else if (selected == Reset) {
|
||||
menuQueue = reset_node_db_menu;
|
||||
screen->runNow();
|
||||
} else if (selected == TraceRoute) {
|
||||
menuQueue = trace_route_menu;
|
||||
screen->runNow();
|
||||
} else if (selected == NodeNameLength) {
|
||||
menuHandler::menuQueue = menuHandler::node_name_length_menu;
|
||||
screen->runNow();
|
||||
@@ -1249,159 +1287,6 @@ void menuHandler::nodeListMenu()
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
}
|
||||
|
||||
void menuHandler::NodePicker()
|
||||
{
|
||||
const char *NODE_PICKER_TITLE;
|
||||
if (currentResolution == ScreenResolution::UltraLow) {
|
||||
NODE_PICKER_TITLE = "Pick Node";
|
||||
} else {
|
||||
NODE_PICKER_TITLE = "Node To Manage";
|
||||
}
|
||||
screen->showNodePicker(NODE_PICKER_TITLE, 30000, [](uint32_t nodenum) -> void {
|
||||
LOG_INFO("Nodenum: %u", nodenum);
|
||||
// Store the selection so the Manage Node menu knows which node to operate on
|
||||
menuHandler::pickedNodeNum = nodenum;
|
||||
// Keep UI favorite context in sync (used elsewhere for some node-based actions)
|
||||
graphics::UIRenderer::currentFavoriteNodeNum = nodenum;
|
||||
menuQueue = Manage_Node_menu;
|
||||
screen->runNow();
|
||||
});
|
||||
}
|
||||
|
||||
void menuHandler::ManageNodeMenu()
|
||||
{
|
||||
// If we don't have a node selected yet, go fast exit
|
||||
auto node = nodeDB->getMeshNode(menuHandler::pickedNodeNum);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
enum optionsNumbers { Back, Favorite, Mute, TraceRoute, KeyVerification, Ignore, enumEnd };
|
||||
static const char *optionsArray[enumEnd] = {"Back"};
|
||||
static int optionsEnumArray[enumEnd] = {Back};
|
||||
int options = 1;
|
||||
|
||||
if (node->is_favorite) {
|
||||
optionsArray[options] = "Unfavorite";
|
||||
} else {
|
||||
optionsArray[options] = "Favorite";
|
||||
}
|
||||
optionsEnumArray[options++] = Favorite;
|
||||
|
||||
bool isMuted = (node->bitfield & NODEINFO_BITFIELD_IS_MUTED_MASK) != 0;
|
||||
if (isMuted) {
|
||||
optionsArray[options] = "Unmute Notifications";
|
||||
} else {
|
||||
optionsArray[options] = "Mute Notifications";
|
||||
}
|
||||
optionsEnumArray[options++] = Mute;
|
||||
|
||||
optionsArray[options] = "Trace Route";
|
||||
optionsEnumArray[options++] = TraceRoute;
|
||||
|
||||
optionsArray[options] = "Key Verification";
|
||||
optionsEnumArray[options++] = KeyVerification;
|
||||
|
||||
if (node->is_ignored) {
|
||||
optionsArray[options] = "Unignore Node";
|
||||
} else {
|
||||
optionsArray[options] = "Ignore Node";
|
||||
}
|
||||
optionsEnumArray[options++] = Ignore;
|
||||
|
||||
BannerOverlayOptions bannerOptions;
|
||||
|
||||
std::string title = "";
|
||||
if (node->has_user && node->user.long_name && node->user.long_name[0]) {
|
||||
title += sanitizeString(node->user.long_name).substr(0, 15);
|
||||
} else {
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof(buf), "%08X", (unsigned int)node->num);
|
||||
title += buf;
|
||||
}
|
||||
bannerOptions.message = title.c_str();
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = options;
|
||||
bannerOptions.optionsEnumPtr = optionsEnumArray;
|
||||
bannerOptions.bannerCallback = [](int selected) -> void {
|
||||
if (selected == Back) {
|
||||
menuQueue = node_base_menu;
|
||||
screen->runNow();
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected == Favorite) {
|
||||
auto n = nodeDB->getMeshNode(menuHandler::pickedNodeNum);
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
if (n->is_favorite) {
|
||||
LOG_INFO("Removing node %08X from favorites", menuHandler::pickedNodeNum);
|
||||
nodeDB->set_favorite(false, menuHandler::pickedNodeNum);
|
||||
} else {
|
||||
LOG_INFO("Adding node %08X to favorites", menuHandler::pickedNodeNum);
|
||||
nodeDB->set_favorite(true, menuHandler::pickedNodeNum);
|
||||
}
|
||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected == Mute) {
|
||||
auto n = nodeDB->getMeshNode(menuHandler::pickedNodeNum);
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (n->bitfield & NODEINFO_BITFIELD_IS_MUTED_MASK) {
|
||||
n->bitfield &= ~NODEINFO_BITFIELD_IS_MUTED_MASK;
|
||||
LOG_INFO("Unmuted node %08X", menuHandler::pickedNodeNum);
|
||||
} else {
|
||||
n->bitfield |= NODEINFO_BITFIELD_IS_MUTED_MASK;
|
||||
LOG_INFO("Muted node %08X", menuHandler::pickedNodeNum);
|
||||
}
|
||||
nodeDB->notifyObservers(true);
|
||||
nodeDB->saveToDisk();
|
||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected == TraceRoute) {
|
||||
LOG_INFO("Starting traceroute to %08X", menuHandler::pickedNodeNum);
|
||||
if (traceRouteModule) {
|
||||
traceRouteModule->startTraceRoute(menuHandler::pickedNodeNum);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected == KeyVerification) {
|
||||
LOG_INFO("Initiating key verification with %08X", menuHandler::pickedNodeNum);
|
||||
if (keyVerificationModule) {
|
||||
keyVerificationModule->sendInitialRequest(menuHandler::pickedNodeNum);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (selected == Ignore) {
|
||||
auto n = nodeDB->getMeshNode(menuHandler::pickedNodeNum);
|
||||
if (!n) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (n->is_ignored) {
|
||||
n->is_ignored = false;
|
||||
LOG_INFO("Unignoring node %08X", menuHandler::pickedNodeNum);
|
||||
} else {
|
||||
n->is_ignored = true;
|
||||
LOG_INFO("Ignoring node %08X", menuHandler::pickedNodeNum);
|
||||
}
|
||||
nodeDB->notifyObservers(true);
|
||||
nodeDB->saveToDisk();
|
||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||
return;
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
}
|
||||
|
||||
void menuHandler::nodeNameLengthMenu()
|
||||
{
|
||||
static const NodeNameOption nodeNameOptions[] = {
|
||||
@@ -2099,6 +1984,21 @@ void menuHandler::shutdownMenu()
|
||||
screen->showOverlayBanner(bannerOptions);
|
||||
}
|
||||
|
||||
void menuHandler::addFavoriteMenu()
|
||||
{
|
||||
const char *NODE_PICKER_TITLE;
|
||||
if (currentResolution == ScreenResolution::UltraLow) {
|
||||
NODE_PICKER_TITLE = "Node Favorite";
|
||||
} else {
|
||||
NODE_PICKER_TITLE = "Node To Favorite";
|
||||
}
|
||||
screen->showNodePicker(NODE_PICKER_TITLE, 30000, [](uint32_t nodenum) -> void {
|
||||
LOG_WARN("Nodenum: %u", nodenum);
|
||||
nodeDB->set_favorite(true, nodenum);
|
||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
|
||||
});
|
||||
}
|
||||
|
||||
void menuHandler::removeFavoriteMenu()
|
||||
{
|
||||
|
||||
@@ -2610,11 +2510,8 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
||||
case shutdown_menu:
|
||||
shutdownMenu();
|
||||
break;
|
||||
case NodePicker_menu:
|
||||
NodePicker();
|
||||
break;
|
||||
case Manage_Node_menu:
|
||||
ManageNodeMenu();
|
||||
case add_favorite:
|
||||
addFavoriteMenu();
|
||||
break;
|
||||
case remove_favorite:
|
||||
removeFavoriteMenu();
|
||||
|
||||
@@ -33,8 +33,7 @@ class menuHandler
|
||||
brightness_picker,
|
||||
reboot_menu,
|
||||
shutdown_menu,
|
||||
NodePicker_menu,
|
||||
Manage_Node_menu,
|
||||
add_favorite,
|
||||
remove_favorite,
|
||||
test_menu,
|
||||
number_test,
|
||||
@@ -56,7 +55,6 @@ class menuHandler
|
||||
DisplayUnits
|
||||
};
|
||||
static screenMenus menuQueue;
|
||||
static uint32_t pickedNodeNum; // node selected by NodePicker for ManageNodeMenu
|
||||
|
||||
static void OnboardMessage();
|
||||
static void LoraRegionPicker(uint32_t duration = 30000);
|
||||
@@ -92,8 +90,6 @@ class menuHandler
|
||||
static void BrightnessPickerMenu();
|
||||
static void rebootMenu();
|
||||
static void shutdownMenu();
|
||||
static void NodePicker();
|
||||
static void ManageNodeMenu();
|
||||
static void addFavoriteMenu();
|
||||
static void removeFavoriteMenu();
|
||||
static void traceRouteMenu();
|
||||
@@ -153,7 +149,6 @@ using GPSToggleOption = MenuOption<meshtastic_Config_PositionConfig_GpsMode>;
|
||||
using GPSFormatOption = MenuOption<meshtastic_DeviceUIConfig_GpsCoordinateFormat>;
|
||||
using NodeNameOption = MenuOption<bool>;
|
||||
using PositionMenuOption = MenuOption<int>;
|
||||
using ManageNodeOption = MenuOption<int>;
|
||||
using ClockFaceOption = MenuOption<bool>;
|
||||
|
||||
} // namespace graphics
|
||||
|
||||
@@ -205,11 +205,9 @@ void drawScrollbar(OLEDDisplay *display, int visibleNodeRows, int totalEntries,
|
||||
void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
|
||||
{
|
||||
bool isLeftCol = (x < SCREEN_WIDTH / 2);
|
||||
int nameMaxWidth = columnWidth - 25;
|
||||
int timeOffset = (currentResolution == ScreenResolution::High) ? (isLeftCol ? 7 : 10) : (isLeftCol ? 3 : 7);
|
||||
|
||||
const char *nodeName = getSafeNodeName(display, node, columnWidth);
|
||||
bool isMuted = (node->bitfield & NODEINFO_BITFIELD_IS_MUTED_MASK) != 0;
|
||||
|
||||
char timeStr[10];
|
||||
uint32_t seconds = sinceLastSeen(node);
|
||||
@@ -236,13 +234,6 @@ void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
|
||||
display->drawXbm(x, y + 5, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint);
|
||||
}
|
||||
}
|
||||
if (node->is_ignored || isMuted) {
|
||||
if (currentResolution == ScreenResolution::High) {
|
||||
display->drawLine(x + 8, y + 8, (isLeftCol ? 0 : x - 4) + nameMaxWidth - 17, y + 8);
|
||||
} else {
|
||||
display->drawLine(x + 4, y + 6, (isLeftCol ? 0 : x - 3) + nameMaxWidth - 4, y + 6);
|
||||
}
|
||||
}
|
||||
|
||||
int rightEdge = x + columnWidth - timeOffset;
|
||||
if (timeStr[strlen(timeStr) - 1] == 'm') // Fix the fact that our fonts don't line up well all the time
|
||||
@@ -262,7 +253,6 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
|
||||
int barsXOffset = columnWidth - barsOffset;
|
||||
|
||||
const char *nodeName = getSafeNodeName(display, node, columnWidth);
|
||||
bool isMuted = (node->bitfield & NODEINFO_BITFIELD_IS_MUTED_MASK) != 0;
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
@@ -275,13 +265,6 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
|
||||
display->drawXbm(x, y + 5, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint);
|
||||
}
|
||||
}
|
||||
if (node->is_ignored || isMuted) {
|
||||
if (currentResolution == ScreenResolution::High) {
|
||||
display->drawLine(x + 8, y + 8, (isLeftCol ? 0 : x - 4) + nameMaxWidth - 17, y + 8);
|
||||
} else {
|
||||
display->drawLine(x + 4, y + 6, (isLeftCol ? 0 : x - 3) + nameMaxWidth - 4, y + 6);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw signal strength bars
|
||||
int bars = (node->snr > 5) ? 4 : (node->snr > 0) ? 3 : (node->snr > -5) ? 2 : (node->snr > -10) ? 1 : 0;
|
||||
@@ -315,7 +298,6 @@ void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
||||
columnWidth - ((currentResolution == ScreenResolution::High) ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22));
|
||||
|
||||
const char *nodeName = getSafeNodeName(display, node, columnWidth);
|
||||
bool isMuted = (node->bitfield & NODEINFO_BITFIELD_IS_MUTED_MASK) != 0;
|
||||
char distStr[10] = "";
|
||||
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
@@ -376,13 +358,6 @@ void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
||||
display->drawXbm(x, y + 5, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint);
|
||||
}
|
||||
}
|
||||
if (node->is_ignored || isMuted) {
|
||||
if (currentResolution == ScreenResolution::High) {
|
||||
display->drawLine(x + 8, y + 8, (isLeftCol ? 0 : x - 4) + nameMaxWidth - 17, y + 8);
|
||||
} else {
|
||||
display->drawLine(x + 4, y + 6, (isLeftCol ? 0 : x - 3) + nameMaxWidth - 4, y + 6);
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen(distStr) > 0) {
|
||||
int offset = (currentResolution == ScreenResolution::High)
|
||||
@@ -417,7 +392,6 @@ void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
||||
columnWidth - ((currentResolution == ScreenResolution::High) ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22));
|
||||
|
||||
const char *nodeName = getSafeNodeName(display, node, columnWidth);
|
||||
bool isMuted = (node->bitfield & NODEINFO_BITFIELD_IS_MUTED_MASK) != 0;
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
@@ -429,13 +403,6 @@ void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
||||
display->drawXbm(x, y + 5, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint);
|
||||
}
|
||||
}
|
||||
if (node->is_ignored || isMuted) {
|
||||
if (currentResolution == ScreenResolution::High) {
|
||||
display->drawLine(x + 8, y + 8, (isLeftCol ? 0 : x - 4) + nameMaxWidth - 17, y + 8);
|
||||
} else {
|
||||
display->drawLine(x + 4, y + 6, (isLeftCol ? 0 : x - 3) + nameMaxWidth - 4, y + 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth, float myHeading,
|
||||
|
||||
@@ -155,6 +155,18 @@ void InkHUD::LogoApplet::onShutdown()
|
||||
// This is then drawn by InkHUD::Events::onShutdown, with a blocking FULL update, after InkHUD's flash write is complete
|
||||
}
|
||||
|
||||
void InkHUD::LogoApplet::onApplyingChanges()
|
||||
{
|
||||
bringToForeground();
|
||||
|
||||
textLeft = "";
|
||||
textRight = "";
|
||||
textTitle = "Applying changes";
|
||||
fontTitle = fontSmall;
|
||||
|
||||
inkhud->forceUpdate(Drivers::EInk::FAST, false);
|
||||
}
|
||||
|
||||
void InkHUD::LogoApplet::onReboot()
|
||||
{
|
||||
bringToForeground();
|
||||
|
||||
@@ -26,6 +26,7 @@ class LogoApplet : public SystemApplet, public concurrency::OSThread
|
||||
void onBackground() override;
|
||||
void onShutdown() override;
|
||||
void onReboot() override;
|
||||
void onApplyingChanges();
|
||||
|
||||
protected:
|
||||
int32_t runOnce() override;
|
||||
|
||||
@@ -22,6 +22,7 @@ enum MenuAction {
|
||||
STORE_CANNEDMESSAGE_SELECTION,
|
||||
SEND_CANNEDMESSAGE,
|
||||
SHUTDOWN,
|
||||
BACK,
|
||||
NEXT_TILE,
|
||||
TOGGLE_BACKLIGHT,
|
||||
TOGGLE_GPS,
|
||||
@@ -36,6 +37,84 @@ enum MenuAction {
|
||||
TOGGLE_NOTIFICATIONS,
|
||||
TOGGLE_INVERT_COLOR,
|
||||
TOGGLE_12H_CLOCK,
|
||||
// Regions
|
||||
SET_REGION_US,
|
||||
SET_REGION_EU_868,
|
||||
SET_REGION_EU_433,
|
||||
SET_REGION_CN,
|
||||
SET_REGION_JP,
|
||||
SET_REGION_ANZ,
|
||||
SET_REGION_KR,
|
||||
SET_REGION_TW,
|
||||
SET_REGION_RU,
|
||||
SET_REGION_IN,
|
||||
SET_REGION_NZ_865,
|
||||
SET_REGION_TH,
|
||||
SET_REGION_LORA_24,
|
||||
SET_REGION_UA_433,
|
||||
SET_REGION_UA_868,
|
||||
SET_REGION_MY_433,
|
||||
SET_REGION_MY_919,
|
||||
SET_REGION_SG_923,
|
||||
SET_REGION_PH_433,
|
||||
SET_REGION_PH_868,
|
||||
SET_REGION_PH_915,
|
||||
SET_REGION_ANZ_433,
|
||||
SET_REGION_KZ_433,
|
||||
SET_REGION_KZ_863,
|
||||
SET_REGION_NP_865,
|
||||
SET_REGION_BR_902,
|
||||
// Device Roles
|
||||
SET_ROLE_CLIENT,
|
||||
SET_ROLE_CLIENT_MUTE,
|
||||
SET_ROLE_ROUTER,
|
||||
SET_ROLE_REPEATER,
|
||||
// Presets
|
||||
SET_PRESET_LONG_SLOW,
|
||||
SET_PRESET_LONG_MODERATE,
|
||||
SET_PRESET_LONG_FAST,
|
||||
SET_PRESET_MEDIUM_SLOW,
|
||||
SET_PRESET_MEDIUM_FAST,
|
||||
SET_PRESET_SHORT_SLOW,
|
||||
SET_PRESET_SHORT_FAST,
|
||||
SET_PRESET_SHORT_TURBO,
|
||||
// Timezones
|
||||
SET_TZ_US_HAWAII,
|
||||
SET_TZ_US_ALASKA,
|
||||
SET_TZ_US_PACIFIC,
|
||||
SET_TZ_US_ARIZONA,
|
||||
SET_TZ_US_MOUNTAIN,
|
||||
SET_TZ_US_CENTRAL,
|
||||
SET_TZ_US_EASTERN,
|
||||
SET_TZ_BR_BRAZILIA,
|
||||
SET_TZ_UTC,
|
||||
SET_TZ_EU_WESTERN,
|
||||
SET_TZ_EU_CENTRAL,
|
||||
SET_TZ_EU_EASTERN,
|
||||
SET_TZ_ASIA_KOLKATA,
|
||||
SET_TZ_ASIA_HONG_KONG,
|
||||
SET_TZ_AU_AWST,
|
||||
SET_TZ_AU_ACST,
|
||||
SET_TZ_AU_AEST,
|
||||
SET_TZ_PACIFIC_NZ,
|
||||
// Power
|
||||
TOGGLE_POWER_SAVE,
|
||||
CALIBRATE_ADC,
|
||||
// Bluetooth
|
||||
TOGGLE_BLUETOOTH,
|
||||
TOGGLE_BLUETOOTH_PAIR_MODE,
|
||||
// Channel
|
||||
TOGGLE_CHANNEL_UPLINK,
|
||||
TOGGLE_CHANNEL_DOWNLINK,
|
||||
TOGGLE_CHANNEL_POSITION,
|
||||
SET_CHANNEL_PRECISION,
|
||||
// Display
|
||||
TOGGLE_DISPLAY_UNITS,
|
||||
// Network
|
||||
TOGGLE_WIFI,
|
||||
// Administration
|
||||
RESET_NODEDB_ALL,
|
||||
RESET_NODEDB_KEEP_FAVORITES,
|
||||
};
|
||||
|
||||
} // namespace NicheGraphics::InkHUD
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -35,6 +35,7 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
|
||||
void onRender() override;
|
||||
|
||||
void show(Tile *t); // Open the menu, onto a user tile
|
||||
void setStartPage(MenuPage page);
|
||||
|
||||
protected:
|
||||
Drivers::LatchingBacklight *backlight = nullptr; // Convenient access to the backlight singleton
|
||||
@@ -56,6 +57,7 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
|
||||
void sendText(NodeNum dest, ChannelIndex channel, const char *message); // Send a text message to mesh
|
||||
void freeCannedMessageResources(); // Clear MenuApplet's canned message processing data
|
||||
|
||||
MenuPage startPageOverride = MenuPage::ROOT;
|
||||
MenuPage currentPage = MenuPage::ROOT;
|
||||
MenuPage previousPage = MenuPage::EXIT;
|
||||
uint8_t cursor = 0; // Which menu item is currently highlighted
|
||||
@@ -63,7 +65,15 @@ class MenuApplet : public SystemApplet, public concurrency::OSThread
|
||||
|
||||
uint16_t systemInfoPanelHeight = 0; // Need to know before we render
|
||||
|
||||
std::vector<MenuItem> items; // MenuItems for the current page. Filled by ShowPage
|
||||
std::vector<MenuItem> items; // MenuItems for the current page. Filled by ShowPage
|
||||
std::vector<std::string> nodeConfigLabels; // Persistent labels for Node Config pages
|
||||
uint8_t selectedChannelIndex = 0; // Currently selected LoRa channel (Node Config → Radio → Channel)
|
||||
bool channelPositionEnabled = false;
|
||||
bool gpsEnabled = false;
|
||||
|
||||
// Recents menu checkbox state (derived from settings.recentlyActiveSeconds)
|
||||
static constexpr uint8_t RECENTS_COUNT = 6;
|
||||
bool recentsSelected[RECENTS_COUNT] = {};
|
||||
|
||||
// Data for selecting and sending canned messages via the menu
|
||||
// Placed into a sub-class for organization only
|
||||
|
||||
@@ -30,6 +30,7 @@ class MenuItem
|
||||
MenuAction action = NO_ACTION;
|
||||
MenuPage nextPage = EXIT;
|
||||
bool *checkState = nullptr;
|
||||
bool isHeader = false; // Non-selectable section label
|
||||
|
||||
// Various constructors, depending on the intended function of the item
|
||||
|
||||
@@ -40,6 +41,12 @@ class MenuItem
|
||||
: label(label), action(action), nextPage(nextPage), checkState(checkState)
|
||||
{
|
||||
}
|
||||
static MenuItem Header(const char *label)
|
||||
{
|
||||
MenuItem item(label, NO_ACTION, EXIT);
|
||||
item.isHeader = true;
|
||||
return item;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace NicheGraphics::InkHUD
|
||||
|
||||
@@ -20,10 +20,27 @@ enum MenuPage : uint8_t {
|
||||
SEND,
|
||||
CANNEDMESSAGE_RECIPIENT, // Select destination for a canned message
|
||||
OPTIONS,
|
||||
NODE_CONFIG,
|
||||
NODE_CONFIG_LORA,
|
||||
NODE_CONFIG_CHANNELS, // List of channels
|
||||
NODE_CONFIG_CHANNEL_DETAIL, // Per-channel options
|
||||
NODE_CONFIG_CHANNEL_PRECISION,
|
||||
NODE_CONFIG_PRESET,
|
||||
NODE_CONFIG_DEVICE,
|
||||
NODE_CONFIG_DEVICE_ROLE,
|
||||
NODE_CONFIG_POWER,
|
||||
NODE_CONFIG_POWER_ADC_CAL,
|
||||
NODE_CONFIG_NETWORK,
|
||||
NODE_CONFIG_DISPLAY,
|
||||
NODE_CONFIG_BLUETOOTH,
|
||||
NODE_CONFIG_POSITION,
|
||||
NODE_CONFIG_ADMIN_RESET,
|
||||
TIMEZONE,
|
||||
APPLETS,
|
||||
AUTOSHOW,
|
||||
RECENTS, // Select length of "recentlyActiveSeconds"
|
||||
EXIT, // Dismiss the menu applet
|
||||
REGION,
|
||||
EXIT, // Dismiss the menu applet
|
||||
};
|
||||
|
||||
} // namespace NicheGraphics::InkHUD
|
||||
|
||||
@@ -10,34 +10,37 @@ using namespace NicheGraphics;
|
||||
|
||||
InkHUD::TipsApplet::TipsApplet()
|
||||
{
|
||||
// Decide which tips (if any) should be shown to user after the boot screen
|
||||
bool needsRegion = (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET);
|
||||
|
||||
bool showTutorialTips = (settings->tips.firstBoot || needsRegion);
|
||||
|
||||
// Welcome screen
|
||||
if (settings->tips.firstBoot)
|
||||
if (showTutorialTips)
|
||||
tipQueue.push_back(Tip::WELCOME);
|
||||
|
||||
// Antenna, region, timezone
|
||||
// Shown at boot if region not yet set
|
||||
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET)
|
||||
// Finish setup
|
||||
if (needsRegion)
|
||||
tipQueue.push_back(Tip::FINISH_SETUP);
|
||||
|
||||
// Using the UI
|
||||
if (showTutorialTips) {
|
||||
tipQueue.push_back(Tip::CUSTOMIZATION);
|
||||
tipQueue.push_back(Tip::BUTTONS);
|
||||
}
|
||||
|
||||
// Shutdown info
|
||||
// Shown until user performs one valid shutdown
|
||||
if (!settings->tips.safeShutdownSeen)
|
||||
tipQueue.push_back(Tip::SAFE_SHUTDOWN);
|
||||
|
||||
// Using the UI
|
||||
if (settings->tips.firstBoot) {
|
||||
tipQueue.push_back(Tip::CUSTOMIZATION);
|
||||
tipQueue.push_back(Tip::BUTTONS);
|
||||
}
|
||||
|
||||
// Catch an incorrect attempt at rotating display
|
||||
if (config.display.flip_screen)
|
||||
tipQueue.push_back(Tip::ROTATION);
|
||||
|
||||
// Applet is foreground immediately at boot, but is obscured by LogoApplet, which is also foreground
|
||||
// LogoApplet can be considered to have a higher Z-index, because it is placed before TipsApplet in the systemApplets vector
|
||||
// Region picker
|
||||
if (needsRegion)
|
||||
tipQueue.push_back(Tip::PICK_REGION);
|
||||
|
||||
if (!tipQueue.empty())
|
||||
bringToForeground();
|
||||
}
|
||||
@@ -51,81 +54,109 @@ void InkHUD::TipsApplet::onRender()
|
||||
|
||||
case Tip::FINISH_SETUP: {
|
||||
setFont(fontMedium);
|
||||
printAt(0, 0, "Tip: Finish Setup");
|
||||
const char *title = "Tip: Finish Setup";
|
||||
uint16_t h = getWrappedTextHeight(0, width(), title);
|
||||
printWrapped(0, 0, width(), title);
|
||||
|
||||
setFont(fontSmall);
|
||||
int16_t cursorY = fontMedium.lineHeight() * 1.5;
|
||||
printAt(0, cursorY, "- connect antenna");
|
||||
int16_t cursorY = h + fontSmall.lineHeight();
|
||||
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- connect a client app");
|
||||
auto drawBullet = [&](const char *text) {
|
||||
uint16_t bh = getWrappedTextHeight(0, width(), text);
|
||||
printWrapped(0, cursorY, width(), text);
|
||||
cursorY += bh + (fontSmall.lineHeight() / 3);
|
||||
};
|
||||
|
||||
// Only if region not set
|
||||
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- set region");
|
||||
}
|
||||
drawBullet("- connect antenna");
|
||||
drawBullet("- connect a client app");
|
||||
|
||||
// Only if tz not set
|
||||
if (!(*config.device.tzdef && config.device.tzdef[0] != 0)) {
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- set timezone");
|
||||
}
|
||||
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET)
|
||||
drawBullet("- set region");
|
||||
|
||||
cursorY += fontSmall.lineHeight() * 1.5;
|
||||
printAt(0, cursorY, "More info at meshtastic.org");
|
||||
if (!(*config.device.tzdef && config.device.tzdef[0] != 0))
|
||||
drawBullet("- set timezone");
|
||||
|
||||
cursorY += fontSmall.lineHeight() / 2;
|
||||
drawBullet("More info at meshtastic.org");
|
||||
|
||||
setFont(fontSmall);
|
||||
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
||||
} break;
|
||||
|
||||
case Tip::PICK_REGION: {
|
||||
setFont(fontMedium);
|
||||
printAt(0, 0, "Set Region");
|
||||
|
||||
setFont(fontSmall);
|
||||
printWrapped(0, fontMedium.lineHeight() * 1.5, width(), "Please select your LoRa region to complete setup.");
|
||||
|
||||
printAt(0, Y(1.0), "Press button to choose", LEFT, BOTTOM);
|
||||
} break;
|
||||
|
||||
case Tip::SAFE_SHUTDOWN: {
|
||||
setFont(fontMedium);
|
||||
printAt(0, 0, "Tip: Shutdown");
|
||||
|
||||
const char *title = "Tip: Shutdown";
|
||||
uint16_t h = getWrappedTextHeight(0, width(), title);
|
||||
printWrapped(0, 0, width(), title);
|
||||
|
||||
setFont(fontSmall);
|
||||
std::string shutdown;
|
||||
shutdown += "Before removing power, please shut down from InkHUD menu, or a client app. \n";
|
||||
shutdown += "\n";
|
||||
shutdown += "This ensures data is saved.";
|
||||
printWrapped(0, fontMedium.lineHeight() * 1.5, width(), shutdown);
|
||||
int16_t cursorY = h + fontSmall.lineHeight();
|
||||
|
||||
const char *body = "Before removing power, please shut down from InkHUD menu, or a client app.\n\n"
|
||||
"This ensures data is saved.";
|
||||
|
||||
uint16_t bodyH = getWrappedTextHeight(0, width(), body);
|
||||
printWrapped(0, cursorY, width(), body);
|
||||
cursorY += bodyH + (fontSmall.lineHeight() / 2);
|
||||
|
||||
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
||||
|
||||
} break;
|
||||
|
||||
case Tip::CUSTOMIZATION: {
|
||||
setFont(fontMedium);
|
||||
printAt(0, 0, "Tip: Customization");
|
||||
|
||||
const char *title = "Tip: Customization";
|
||||
uint16_t h = getWrappedTextHeight(0, width(), title);
|
||||
printWrapped(0, 0, width(), title);
|
||||
|
||||
setFont(fontSmall);
|
||||
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
|
||||
"Configure & control display with the InkHUD menu. Optional features, layout, rotation, and more.");
|
||||
int16_t cursorY = h + fontSmall.lineHeight();
|
||||
|
||||
const char *body = "Configure & control display with the InkHUD menu. "
|
||||
"Optional features, layout, rotation, and more.";
|
||||
|
||||
uint16_t bodyH = getWrappedTextHeight(0, width(), body);
|
||||
printWrapped(0, cursorY, width(), body);
|
||||
cursorY += bodyH + (fontSmall.lineHeight() / 2);
|
||||
|
||||
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
||||
} break;
|
||||
|
||||
case Tip::BUTTONS: {
|
||||
setFont(fontMedium);
|
||||
printAt(0, 0, "Tip: Buttons");
|
||||
|
||||
const char *title = "Tip: Buttons";
|
||||
uint16_t h = getWrappedTextHeight(0, width(), title);
|
||||
printWrapped(0, 0, width(), title);
|
||||
|
||||
setFont(fontSmall);
|
||||
int16_t cursorY = fontMedium.lineHeight() * 1.5;
|
||||
int16_t cursorY = h + fontSmall.lineHeight();
|
||||
|
||||
auto drawBullet = [&](const char *text) {
|
||||
uint16_t bh = getWrappedTextHeight(0, width(), text);
|
||||
printWrapped(0, cursorY, width(), text);
|
||||
cursorY += bh + (fontSmall.lineHeight() / 3);
|
||||
};
|
||||
|
||||
if (!settings->joystick.enabled) {
|
||||
printAt(0, cursorY, "User Button");
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- short press: next");
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- long press: select / open menu");
|
||||
drawBullet("User Button");
|
||||
drawBullet("- short press: next");
|
||||
drawBullet("- long press: select or open menu");
|
||||
} else {
|
||||
printAt(0, cursorY, "Joystick");
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- open menu / select");
|
||||
cursorY += fontSmall.lineHeight() * 1.5;
|
||||
printAt(0, cursorY, "Exit Button");
|
||||
cursorY += fontSmall.lineHeight() * 1.2;
|
||||
printAt(0, cursorY, "- switch tile / close menu");
|
||||
drawBullet("Joystick");
|
||||
drawBullet("- press: open menu or select");
|
||||
drawBullet("Exit Button");
|
||||
drawBullet("- press: switch tile or close menu");
|
||||
}
|
||||
|
||||
printAt(0, Y(1.0), "Press button to continue", LEFT, BOTTOM);
|
||||
@@ -133,12 +164,21 @@ void InkHUD::TipsApplet::onRender()
|
||||
|
||||
case Tip::ROTATION: {
|
||||
setFont(fontMedium);
|
||||
printAt(0, 0, "Tip: Rotation");
|
||||
|
||||
const char *title = "Tip: Rotation";
|
||||
uint16_t h = getWrappedTextHeight(0, width(), title);
|
||||
printWrapped(0, 0, width(), title);
|
||||
|
||||
setFont(fontSmall);
|
||||
if (!settings->joystick.enabled) {
|
||||
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
|
||||
"To rotate the display, use the InkHUD menu. Long-press the user button > Options > Rotate.");
|
||||
int16_t cursorY = h + fontSmall.lineHeight();
|
||||
|
||||
const char *body = "To rotate the display, use the InkHUD menu. "
|
||||
"Long-press the user button > Options > Rotate.";
|
||||
|
||||
uint16_t bh = getWrappedTextHeight(0, width(), body);
|
||||
printWrapped(0, cursorY, width(), body);
|
||||
cursorY += bh + (fontSmall.lineHeight() / 2);
|
||||
} else {
|
||||
printWrapped(0, fontMedium.lineHeight() * 1.5, width(),
|
||||
"To rotate the display, use the InkHUD menu. Press the user button > Options > Rotate.");
|
||||
@@ -159,12 +199,15 @@ void InkHUD::TipsApplet::renderWelcome()
|
||||
{
|
||||
uint16_t padW = X(0.05);
|
||||
|
||||
// Detect portrait orientation
|
||||
bool portrait = height() > width();
|
||||
|
||||
// Block 1 - logo & title
|
||||
// ========================
|
||||
|
||||
// Logo size
|
||||
uint16_t logoWLimit = X(0.3);
|
||||
uint16_t logoHLimit = Y(0.3);
|
||||
uint16_t logoWLimit = portrait ? X(0.5) : X(0.3);
|
||||
uint16_t logoHLimit = portrait ? Y(0.25) : Y(0.3);
|
||||
uint16_t logoW = getLogoWidth(logoWLimit, logoHLimit);
|
||||
uint16_t logoH = getLogoHeight(logoWLimit, logoHLimit);
|
||||
|
||||
@@ -177,7 +220,7 @@ void InkHUD::TipsApplet::renderWelcome()
|
||||
|
||||
// Center the block
|
||||
// Desired effect: equal margin from display edge for logo left and title right
|
||||
int16_t block1Y = Y(0.3);
|
||||
int16_t block1Y = portrait ? Y(0.2) : Y(0.3);
|
||||
int16_t block1CX = X(0.5) + (logoW / 2) - (titleW / 2);
|
||||
int16_t logoCX = block1CX - (logoW / 2) - (padW / 2);
|
||||
int16_t titleCX = block1CX + (titleW / 2) + (padW / 2);
|
||||
@@ -192,7 +235,7 @@ void InkHUD::TipsApplet::renderWelcome()
|
||||
std::string subtitle = "InkHUD";
|
||||
if (width() >= 200)
|
||||
subtitle += " - A Heads-Up Display"; // Future proofing: narrower for tiny display
|
||||
printAt(X(0.5), Y(0.6), subtitle, CENTER, MIDDLE);
|
||||
printAt(X(0.5), portrait ? Y(0.45) : Y(0.6), subtitle, CENTER, MIDDLE);
|
||||
|
||||
// Block 3 - press to continue
|
||||
// ============================
|
||||
@@ -224,26 +267,37 @@ void InkHUD::TipsApplet::onBackground()
|
||||
// While our SystemApplet::handleInput flag is true
|
||||
void InkHUD::TipsApplet::onButtonShortPress()
|
||||
{
|
||||
bool needsRegion = (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET);
|
||||
// If we're prompting the user to pick a region, hand off to the menu
|
||||
if (!tipQueue.empty() && tipQueue.front() == Tip::PICK_REGION) {
|
||||
tipQueue.pop_front();
|
||||
|
||||
// Signal InkHUD to open the menu on Region page
|
||||
inkhud->forceRegionMenu = true;
|
||||
|
||||
// Close tips and open menu
|
||||
sendToBackground();
|
||||
inkhud->openMenu();
|
||||
return;
|
||||
}
|
||||
// Consume current tip
|
||||
tipQueue.pop_front();
|
||||
|
||||
// All tips done
|
||||
if (tipQueue.empty()) {
|
||||
// Record that user has now seen the "tutorial" set of tips
|
||||
// Don't show them on subsequent boots
|
||||
if (settings->tips.firstBoot) {
|
||||
if (settings->tips.firstBoot && !needsRegion) {
|
||||
settings->tips.firstBoot = false;
|
||||
inkhud->persistence->saveSettings();
|
||||
}
|
||||
|
||||
// Close applet, and full refresh to clean the screen
|
||||
// Need to force update, because our request would be ignored otherwise, as we are now background
|
||||
// Close applet and clean the screen
|
||||
sendToBackground();
|
||||
inkhud->forceUpdate(EInk::UpdateTypes::FULL);
|
||||
}
|
||||
|
||||
// More tips left
|
||||
else
|
||||
} else {
|
||||
requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
// Functions the same as the user button in this instance
|
||||
|
||||
@@ -23,6 +23,7 @@ class TipsApplet : public SystemApplet
|
||||
enum class Tip {
|
||||
WELCOME,
|
||||
FINISH_SETUP,
|
||||
PICK_REGION,
|
||||
SAFE_SHUTDOWN,
|
||||
CUSTOMIZATION,
|
||||
BUTTONS,
|
||||
|
||||
@@ -276,6 +276,15 @@ int InkHUD::Events::beforeDeepSleep(void *unused)
|
||||
return 0; // We agree: deep sleep now
|
||||
}
|
||||
|
||||
// Display an intermediate screen while configuration changes are applied
|
||||
void InkHUD::Events::applyingChanges()
|
||||
{
|
||||
// Bring the logo applet forward with a temporary message
|
||||
for (SystemApplet *sa : inkhud->systemApplets) {
|
||||
sa->onApplyingChanges();
|
||||
}
|
||||
}
|
||||
|
||||
// Callback for rebootObserver
|
||||
// Same as shutdown, without drawing the logoApplet
|
||||
// Makes sure we don't lose message history / InkHUD config
|
||||
|
||||
@@ -29,12 +29,13 @@ class Events
|
||||
|
||||
void onButtonShort(); // User button: short press
|
||||
void onButtonLong(); // User button: long press
|
||||
void onExitShort(); // Exit button: short press
|
||||
void onExitLong(); // Exit button: long press
|
||||
void onNavUp(); // Navigate up
|
||||
void onNavDown(); // Navigate down
|
||||
void onNavLeft(); // Navigate left
|
||||
void onNavRight(); // Navigate right
|
||||
void applyingChanges();
|
||||
void onExitShort(); // Exit button: short press
|
||||
void onExitLong(); // Exit button: long press
|
||||
void onNavUp(); // Navigate up
|
||||
void onNavDown(); // Navigate down
|
||||
void onNavLeft(); // Navigate left
|
||||
void onNavRight(); // Navigate right
|
||||
|
||||
int beforeDeepSleep(void *unused); // Prepare for shutdown
|
||||
int beforeReboot(void *unused); // Prepare for reboot
|
||||
|
||||
@@ -53,6 +53,13 @@ void InkHUD::InkHUD::addApplet(const char *name, Applet *a, bool defaultActive,
|
||||
windowManager->addApplet(name, a, defaultActive, defaultAutoshow, onTile);
|
||||
}
|
||||
|
||||
void InkHUD::InkHUD::notifyApplyingChanges()
|
||||
{
|
||||
if (events) {
|
||||
events->applyingChanges();
|
||||
}
|
||||
}
|
||||
|
||||
// Start InkHUD!
|
||||
// Call this only after you have configured InkHUD
|
||||
void InkHUD::InkHUD::begin()
|
||||
|
||||
@@ -47,6 +47,7 @@ class InkHUD
|
||||
void setDriver(Drivers::EInk *driver);
|
||||
void setDisplayResilience(uint8_t fastPerFull = 5, float stressMultiplier = 2.0);
|
||||
void addApplet(const char *name, Applet *a, bool defaultActive = false, bool defaultAutoshow = false, uint8_t onTile = -1);
|
||||
void notifyApplyingChanges();
|
||||
|
||||
void begin();
|
||||
|
||||
@@ -76,6 +77,9 @@ class InkHUD
|
||||
void rotateJoystick(uint8_t angle = 1); // rotate 90 deg by default
|
||||
void toggleBatteryIcon();
|
||||
|
||||
// Used by TipsApplet to force menu to start on Region selection
|
||||
bool forceRegionMenu = false;
|
||||
|
||||
// Updating the display
|
||||
// - called by various InkHUD components
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ class SystemApplet : public Applet
|
||||
bool lockRequests = false; // - prevent other applets from triggering display updates
|
||||
|
||||
virtual void onReboot() { onShutdown(); } // - handle reboot specially
|
||||
virtual void onApplyingChanges() {}
|
||||
|
||||
// Other system applets may take precedence over our own system applet though
|
||||
// The order an applet is passed to WindowManager::addSystemApplet determines this hierarchy (added earlier = higher rank)
|
||||
|
||||
@@ -38,9 +38,6 @@
|
||||
#include "target_specific.h"
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#if HAS_SCREEN
|
||||
#include "MessageStore.h"
|
||||
#endif
|
||||
|
||||
#ifdef ELECROW_ThinkNode_M5
|
||||
PCA9557 io(0x18, &Wire);
|
||||
@@ -1655,9 +1652,6 @@ void loop()
|
||||
if (dispdev)
|
||||
static_cast<TFTDisplay *>(dispdev)->sdlLoop();
|
||||
}
|
||||
#endif
|
||||
#if HAS_SCREEN && ENABLE_MESSAGE_PERSISTENCE
|
||||
messageStoreAutosaveTick();
|
||||
#endif
|
||||
long delayMsec = mainController.runOrDelay();
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ template <typename T> bool LR11x0Interface<T>::reconfigure()
|
||||
return RADIOLIB_ERR_NONE;
|
||||
}
|
||||
|
||||
template <typename T> void LR11x0Interface<T>::disableInterrupt()
|
||||
template <typename T> void INTERRUPT_ATTR LR11x0Interface<T>::disableInterrupt()
|
||||
{
|
||||
lora.clearIrqAction();
|
||||
}
|
||||
|
||||
@@ -378,8 +378,6 @@ extern meshtastic_CriticalErrorCode error_code;
|
||||
extern uint32_t error_address;
|
||||
#define NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_SHIFT 0
|
||||
#define NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK (1 << NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_SHIFT)
|
||||
#define NODEINFO_BITFIELD_IS_MUTED_SHIFT 1
|
||||
#define NODEINFO_BITFIELD_IS_MUTED_MASK (1 << NODEINFO_BITFIELD_IS_MUTED_SHIFT)
|
||||
|
||||
#define Module_Config_size \
|
||||
(ModuleConfig_CannedMessageConfig_size + ModuleConfig_ExternalNotificationConfig_size + ModuleConfig_MQTTConfig_size + \
|
||||
|
||||
@@ -193,7 +193,7 @@ bool RF95Interface::init()
|
||||
return res == RADIOLIB_ERR_NONE;
|
||||
}
|
||||
|
||||
void RF95Interface::disableInterrupt()
|
||||
void INTERRUPT_ATTR RF95Interface::disableInterrupt()
|
||||
{
|
||||
lora->clearDio0Action();
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ template <typename T> bool SX126xInterface<T>::reconfigure()
|
||||
return RADIOLIB_ERR_NONE;
|
||||
}
|
||||
|
||||
template <typename T> void SX126xInterface<T>::disableInterrupt()
|
||||
template <typename T> void INTERRUPT_ATTR SX126xInterface<T>::disableInterrupt()
|
||||
{
|
||||
lora.clearDio1Action();
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ template <typename T> bool SX128xInterface<T>::reconfigure()
|
||||
return RADIOLIB_ERR_NONE;
|
||||
}
|
||||
|
||||
template <typename T> void SX128xInterface<T>::disableInterrupt()
|
||||
template <typename T> void INTERRUPT_ATTR SX128xInterface<T>::disableInterrupt()
|
||||
{
|
||||
lora.clearDio1Action();
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo
|
||||
info.is_favorite = lite->is_favorite;
|
||||
info.is_ignored = lite->is_ignored;
|
||||
info.is_key_manually_verified = lite->bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
|
||||
info.is_muted = lite->bitfield & NODEINFO_BITFIELD_IS_MUTED_MASK;
|
||||
|
||||
if (lite->has_hops_away) {
|
||||
info.has_hops_away = true;
|
||||
|
||||
@@ -21,7 +21,7 @@ uint32_t ntp_renew = 0;
|
||||
#endif
|
||||
|
||||
EthernetUDP syslogClient;
|
||||
meshtastic::Syslog syslog(syslogClient);
|
||||
Syslog syslog(syslogClient);
|
||||
|
||||
bool ethStartupComplete = 0;
|
||||
|
||||
|
||||
@@ -281,6 +281,8 @@ typedef struct _meshtastic_AdminMessage {
|
||||
meshtastic_SharedContact add_contact;
|
||||
/* Initiate or respond to a key verification request */
|
||||
meshtastic_KeyVerificationAdmin key_verification;
|
||||
/* Tell the node to reboot into OTA mode for firmware update via BLE or WiFi (ESP32 only for now) */
|
||||
meshtastic_OTAMode reboot_ota_mode;
|
||||
/* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */
|
||||
int32_t factory_reset_device;
|
||||
/* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
|
||||
@@ -339,6 +341,7 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_payload_variant_backup_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
|
||||
#define meshtastic_AdminMessage_payload_variant_restore_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
|
||||
#define meshtastic_AdminMessage_payload_variant_remove_backup_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
|
||||
#define meshtastic_AdminMessage_payload_variant_reboot_ota_mode_ENUMTYPE meshtastic_OTAMode
|
||||
|
||||
|
||||
#define meshtastic_AdminMessage_OTAEvent_reboot_ota_mode_ENUMTYPE meshtastic_OTAMode
|
||||
@@ -433,6 +436,7 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_commit_edit_settings_tag 65
|
||||
#define meshtastic_AdminMessage_add_contact_tag 66
|
||||
#define meshtastic_AdminMessage_key_verification_tag 67
|
||||
#define meshtastic_AdminMessage_reboot_ota_mode_tag 68
|
||||
#define meshtastic_AdminMessage_factory_reset_device_tag 94
|
||||
#define meshtastic_AdminMessage_reboot_ota_seconds_tag 95
|
||||
#define meshtastic_AdminMessage_exit_simulator_tag 96
|
||||
@@ -493,6 +497,7 @@ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_ed
|
||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,add_contact,add_contact), 66) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,key_verification,key_verification), 67) \
|
||||
X(a, STATIC, ONEOF, UENUM, (payload_variant,reboot_ota_mode,reboot_ota_mode), 68) \
|
||||
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \
|
||||
X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \
|
||||
|
||||
@@ -296,8 +296,6 @@ typedef enum _meshtastic_HardwareModel {
|
||||
meshtastic_HardwareModel_THINKNODE_M6 = 120,
|
||||
/* Elecrow Meshstick 1262 */
|
||||
meshtastic_HardwareModel_MESHSTICK_1262 = 121,
|
||||
/* LilyGo T-Beam 1W */
|
||||
meshtastic_HardwareModel_TBEAM_1_WATT = 122,
|
||||
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
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.
|
||||
------------------------------------------------------------------------------------------------------------------------------------------ */
|
||||
@@ -1368,6 +1366,10 @@ extern "C" {
|
||||
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MAX meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF
|
||||
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_ARRAYSIZE ((meshtastic_StoreForwardPlusPlus_SFPP_message_type)(meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF+1))
|
||||
|
||||
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE
|
||||
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MAX meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF
|
||||
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_ARRAYSIZE ((meshtastic_StoreForwardPlusPlus_SFPP_message_type)(meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF+1))
|
||||
|
||||
#define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET
|
||||
#define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX
|
||||
#define _meshtastic_MeshPacket_Priority_ARRAYSIZE ((meshtastic_MeshPacket_Priority)(meshtastic_MeshPacket_Priority_MAX+1))
|
||||
|
||||
@@ -84,11 +84,8 @@ typedef enum _meshtastic_ModuleConfig_SerialConfig_Serial_Mode {
|
||||
https://beta.ivc.no/wiki/index.php/Victron_VE_Direct_DIY_Cable */
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_VE_DIRECT = 7,
|
||||
/* Used to configure and view some parameters of MeshSolar.
|
||||
https://heltec.org/project/meshsolar/ */
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG = 8,
|
||||
/* Logs mesh traffic to the serial pins, ideal for logging via openLog or similar. */
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_LOG = 9, /* includes other packets */
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_LOGTEXT = 10 /* only text (channel & DM) */
|
||||
https://heltec.org/project/meshsolar/ */
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG = 8
|
||||
} meshtastic_ModuleConfig_SerialConfig_Serial_Mode;
|
||||
|
||||
/* TODO: REPLACE */
|
||||
@@ -486,8 +483,8 @@ extern "C" {
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Baud)(meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600+1))
|
||||
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN meshtastic_ModuleConfig_SerialConfig_Serial_Mode_DEFAULT
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MAX meshtastic_ModuleConfig_SerialConfig_Serial_Mode_LOGTEXT
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Mode)(meshtastic_ModuleConfig_SerialConfig_Serial_Mode_LOGTEXT+1))
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MAX meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Mode)(meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG+1))
|
||||
|
||||
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE
|
||||
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MAX meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK
|
||||
|
||||
@@ -58,7 +58,7 @@ bool needReconnect = true; // If we create our reconnector, run it once at the
|
||||
bool isReconnecting = false; // If we are currently reconnecting
|
||||
|
||||
WiFiUDP syslogClient;
|
||||
meshtastic::Syslog syslog(syslogClient);
|
||||
Syslog syslog(syslogClient);
|
||||
|
||||
Periodic *wifiReconnect;
|
||||
|
||||
|
||||
@@ -383,16 +383,6 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_AdminMessage_toggle_muted_node_tag: {
|
||||
LOG_INFO("Client received toggle_muted_node command");
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->toggle_muted_node);
|
||||
if (node != NULL) {
|
||||
node->bitfield ^= (1 << NODEINFO_BITFIELD_IS_MUTED_SHIFT);
|
||||
saveChanges(SEGMENT_NODEDATABASE, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case meshtastic_AdminMessage_set_fixed_position_tag: {
|
||||
LOG_INFO("Client received set_fixed_position command");
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
|
||||
@@ -459,13 +459,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
||||
}
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(mp.from);
|
||||
bool mutedNode = false;
|
||||
if (sender) {
|
||||
mutedNode = (sender->bitfield & NODEINFO_BITFIELD_IS_MUTED_MASK);
|
||||
}
|
||||
meshtastic_Channel ch = channels.getByIndex(mp.channel ? mp.channel : channels.getPrimaryIndex());
|
||||
|
||||
if (moduleConfig.external_notification.alert_bell) {
|
||||
if (containsBell) {
|
||||
LOG_INFO("externalNotificationModule - Notification Bell");
|
||||
@@ -516,7 +510,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleConfig.external_notification.alert_message && !mutedNode &&
|
||||
if (moduleConfig.external_notification.alert_message &&
|
||||
(!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) {
|
||||
LOG_INFO("externalNotificationModule - Notification Module");
|
||||
isNagging = true;
|
||||
@@ -528,7 +522,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleConfig.external_notification.alert_message_vibra && !mutedNode &&
|
||||
if (moduleConfig.external_notification.alert_message_vibra &&
|
||||
(!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) {
|
||||
LOG_INFO("externalNotificationModule - Notification Module (Vibra)");
|
||||
isNagging = true;
|
||||
@@ -540,7 +534,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleConfig.external_notification.alert_message_buzzer && !mutedNode &&
|
||||
if (moduleConfig.external_notification.alert_message_buzzer &&
|
||||
(!ch.settings.has_module_settings || !ch.settings.module_settings.is_muted)) {
|
||||
LOG_INFO("externalNotificationModule - Notification Module (Buzzer)");
|
||||
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "ProtobufModule.h"
|
||||
#include "configuration.h"
|
||||
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_PAXCOUNTER
|
||||
#if defined(ARCH_ESP32)
|
||||
#include "../mesh/generated/meshtastic/paxcount.pb.h"
|
||||
#include "NodeDB.h"
|
||||
#include <libpax_api.h>
|
||||
@@ -35,4 +35,4 @@ class PaxcounterModule : private concurrency::OSThread, public ProtobufModule<me
|
||||
};
|
||||
|
||||
extern PaxcounterModule *paxcounterModule;
|
||||
#endif
|
||||
#endif
|
||||
@@ -313,11 +313,11 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
{
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
#ifdef DEBUG_NIMBLE_NOTIFY
|
||||
|
||||
int currentNotifyCount = notifyCount.fetch_add(1);
|
||||
|
||||
uint8_t cc = bleServer->getConnectedCount();
|
||||
|
||||
#ifdef DEBUG_NIMBLE_NOTIFY
|
||||
// This logging slows things down when there are lots of packets going to the phone, like initial connection:
|
||||
LOG_DEBUG("BLE notify(%d) fromNum: %d connections: %d", currentNotifyCount, fromRadioNum, cc);
|
||||
#endif
|
||||
|
||||
@@ -487,11 +487,6 @@ void portduinoSetup()
|
||||
max_GPIO = i->pin;
|
||||
}
|
||||
|
||||
for (auto i : portduino_config.extra_pins) {
|
||||
if (i.enabled && i.pin > max_GPIO)
|
||||
max_GPIO = i.pin;
|
||||
}
|
||||
|
||||
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
|
||||
@@ -509,19 +504,6 @@ void portduinoSetup()
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto i : portduino_config.extra_pins) {
|
||||
// In the case of a ch341 Lora device, we don't want to touch the system GPIO lines for Lora
|
||||
// Those GPIO are handled in our usermode driver instead.
|
||||
if (i.config_section == "Lora" && portduino_config.lora_spi_dev == "ch341") {
|
||||
continue;
|
||||
}
|
||||
if (i.enabled) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only initialize the radio pins when dealing with real, kernel controlled SPI hardware
|
||||
if (portduino_config.lora_spi_dev != "" && portduino_config.lora_spi_dev != "ch341") {
|
||||
@@ -735,16 +717,6 @@ bool loadConfig(const char *configPath)
|
||||
portduino_config.has_gps = 1;
|
||||
}
|
||||
}
|
||||
if (yamlConfig["GPIO"]["ExtraPins"]) {
|
||||
for (auto extra_pin : yamlConfig["GPIO"]["ExtraPins"]) {
|
||||
portduino_config.extra_pins.push_back(pinMapping());
|
||||
portduino_config.extra_pins.back().config_section = "GPIO";
|
||||
portduino_config.extra_pins.back().config_name = "ExtraPins";
|
||||
portduino_config.extra_pins.back().enabled = true;
|
||||
readGPIOFromYaml(extra_pin, portduino_config.extra_pins.back());
|
||||
}
|
||||
}
|
||||
|
||||
if (yamlConfig["I2C"]) {
|
||||
portduino_config.i2cdev = yamlConfig["I2C"]["I2CDevice"].as<std::string>("");
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "LR11x0Interface.h"
|
||||
#include "Module.h"
|
||||
@@ -98,7 +97,6 @@ extern struct portduino_config_struct {
|
||||
pinMapping lora_txen_pin = {"Lora", "TXen"};
|
||||
pinMapping lora_rxen_pin = {"Lora", "RXen"};
|
||||
pinMapping lora_sx126x_ant_sw_pin = {"Lora", "SX126X_ANT_SW"};
|
||||
std::vector<pinMapping> extra_pins = {};
|
||||
|
||||
// GPS
|
||||
bool has_gps = false;
|
||||
@@ -302,20 +300,6 @@ extern struct portduino_config_struct {
|
||||
}
|
||||
out << YAML::EndMap; // Lora
|
||||
|
||||
if (!extra_pins.empty()) {
|
||||
out << YAML::Key << "GPIO" << YAML::Value << YAML::BeginMap;
|
||||
out << YAML::Key << "ExtraPins" << YAML::Value << YAML::BeginSeq;
|
||||
for (auto extra : extra_pins) {
|
||||
out << YAML::BeginMap;
|
||||
out << YAML::Key << "pin" << YAML::Value << extra.pin;
|
||||
out << YAML::Key << "line" << YAML::Value << extra.line;
|
||||
out << YAML::Key << "gpiochip" << YAML::Value << extra.gpiochip;
|
||||
out << YAML::EndMap;
|
||||
}
|
||||
out << YAML::EndSeq;
|
||||
out << YAML::EndMap; // GPIO
|
||||
}
|
||||
|
||||
if (i2cdev != "") {
|
||||
out << YAML::Key << "I2C" << YAML::Value << YAML::BeginMap;
|
||||
out << YAML::Key << "I2CDevice" << YAML::Value << i2cdev;
|
||||
|
||||
@@ -65,6 +65,8 @@ lib_deps =
|
||||
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
|
||||
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
|
||||
https://github.com/lewisxhe/XPowersLib/archive/v0.3.2.zip
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
|
||||
rweather/Crypto@0.4.0
|
||||
|
||||
|
||||
@@ -5,5 +5,4 @@ extends = esp32_common
|
||||
custom_esp32_kind = esp32
|
||||
|
||||
build_flags =
|
||||
${esp32_common.build_flags}
|
||||
-DMESHTASTIC_EXCLUDE_AUDIO=1
|
||||
${esp32_common.build_flags}
|
||||
@@ -4,8 +4,3 @@ custom_esp32_kind = esp32c3
|
||||
|
||||
monitor_speed = 115200
|
||||
monitor_filters = esp32_c3_exception_decoder
|
||||
|
||||
lib_deps =
|
||||
${esp32_common.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
|
||||
@@ -12,12 +12,7 @@ build_flags =
|
||||
-DHAS_BLUETOOTH=0
|
||||
-DMESHTASTIC_EXCLUDE_PAXCOUNTER
|
||||
-DMESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
|
||||
lib_deps =
|
||||
${esp32_common.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
|
||||
|
||||
lib_ignore =
|
||||
${esp32_common.lib_ignore}
|
||||
NimBLE-Arduino
|
||||
|
||||
@@ -27,6 +27,6 @@ build_flags =
|
||||
|
||||
lib_deps = ${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/1655054ba298e0e29fc2044741940f927f9c2a43.zip
|
||||
# renovate: datasource=custom.pio depName=PCA9557-arduino packageName=maxpromer/library/PCA9557-arduino
|
||||
maxpromer/PCA9557-arduino@1.0.0
|
||||
|
||||
@@ -26,7 +26,7 @@ build_flags =
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/33db3fa8ee6fc47d160bdb44f8f127c9a9203a10.zip
|
||||
|
||||
[env:crowpanel-esp32s3-4-epaper]
|
||||
extends = esp32s3_base
|
||||
@@ -56,7 +56,7 @@ build_flags =
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/33db3fa8ee6fc47d160bdb44f8f127c9a9203a10.zip
|
||||
|
||||
[env:crowpanel-esp32s3-2-epaper]
|
||||
extends = esp32s3_base
|
||||
@@ -86,4 +86,4 @@ build_flags =
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/33db3fa8ee6fc47d160bdb44f8f127c9a9203a10.zip
|
||||
|
||||
@@ -45,9 +45,10 @@ lib_deps = ${esp32s3_base.lib_deps}
|
||||
earlephilhower/ESP8266Audio@1.9.9
|
||||
# renovate: datasource=custom.pio depName=ESP8266SAM packageName=earlephilhower/library/ESP8266SAM
|
||||
earlephilhower/ESP8266SAM@1.0.1
|
||||
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
|
||||
lovyan03/LovyanGFX@1.2.0 ; note: v1.2.7 breaks the elecrow 7" display functionality
|
||||
# renovate: datasource=custom.pio depName=TCA9534 packageName=hideakitai/library/TCA9534
|
||||
hideakitai/TCA9534@0.1.1
|
||||
lovyan03/LovyanGFX@1.2.0 ; note: v1.2.7 breaks the elecrow 7" display functionality
|
||||
|
||||
[crowpanel_small_esp32s3_base] ; 2.4, 2.8, 3.5 inch
|
||||
extends = crowpanel_base
|
||||
|
||||
@@ -3,8 +3,3 @@ extends = esp32_common
|
||||
custom_esp32_kind = esp32s3
|
||||
|
||||
monitor_speed = 115200
|
||||
|
||||
lib_deps =
|
||||
${esp32_common.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
|
||||
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
|
||||
|
||||
@@ -29,7 +29,7 @@ build_flags =
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/1655054ba298e0e29fc2044741940f927f9c2a43.zip
|
||||
upload_speed = 115200
|
||||
|
||||
[env:heltec-vision-master-e213-inkhud]
|
||||
|
||||
@@ -32,7 +32,7 @@ build_flags =
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/448c8538129fde3d02a7cb5e6fc81971ad92547f.zip
|
||||
upload_speed = 115200
|
||||
|
||||
[env:heltec-vision-master-e290-inkhud]
|
||||
|
||||
@@ -29,7 +29,7 @@ build_flags =
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/1655054ba298e0e29fc2044741940f927f9c2a43.zip
|
||||
upload_speed = 115200
|
||||
|
||||
[env:heltec-wireless-paper-inkhud]
|
||||
|
||||
@@ -26,5 +26,5 @@ build_flags =
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/55f618961db45a23eff0233546430f1e5a80f63a.zip
|
||||
upload_speed = 115200
|
||||
|
||||
@@ -29,7 +29,7 @@ build_flags =
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2
|
||||
zinggjm/GxEPD2@1.6.5
|
||||
zinggjm/GxEPD2@1.6.4
|
||||
# renovate: datasource=git-refs depName=CSE_Touch packageName=https://github.com/CIRCUITSTATE/CSE_Touch gitBranch=main
|
||||
https://github.com/CIRCUITSTATE/CSE_Touch/archive/b44f23b6f870b848f1fbe453c190879bc6cfaafa.zip
|
||||
# renovate: datasource=github-tags depName=CSE_CST328 packageName=CIRCUITSTATE/CSE_CST328
|
||||
|
||||
@@ -31,7 +31,7 @@ build_flags =
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/b202ebfec6a4821e098cf7a625ba0f6f2400292d.zip
|
||||
|
||||
[env:tlora-t3s3-epaper-inkhud]
|
||||
extends = esp32s3_base, inkhud
|
||||
|
||||
@@ -33,7 +33,7 @@ build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/ELECROW
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/33db3fa8ee6fc47d160bdb44f8f127c9a9203a10.zip
|
||||
# renovate: datasource=custom.pio depName=nRF52_PWM packageName=khoih-prog/library/nRF52_PWM
|
||||
khoih-prog/nRF52_PWM@1.0.1
|
||||
;upload_protocol = fs
|
||||
|
||||
@@ -38,7 +38,7 @@ build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/b202ebfec6a4821e098cf7a625ba0f6f2400292d.zip
|
||||
|
||||
[env:heltec-mesh-pocket-5000-inkhud]
|
||||
extends = nrf52840_base, inkhud
|
||||
@@ -101,7 +101,7 @@ build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/b202ebfec6a4821e098cf7a625ba0f6f2400292d.zip
|
||||
|
||||
[env:heltec-mesh-pocket-10000-inkhud]
|
||||
extends = nrf52840_base, inkhud
|
||||
|
||||
@@ -16,7 +16,7 @@ lib_deps =
|
||||
# renovate: datasource=git-refs depName=NMIoT-meshsolar packageName=https://github.com/NMIoT/meshsolar gitBranch=main
|
||||
https://github.com/NMIoT/meshsolar/archive/dfc5330dad443982e6cdd37a61d33fc7252f468b.zip
|
||||
# renovate: datasource=custom.pio depName=ArduinoJson packageName=bblanchon/library/ArduinoJson
|
||||
bblanchon/ArduinoJson@6.21.5
|
||||
bblanchon/ArduinoJson@6.21.4
|
||||
|
||||
[env:heltec-mesh-solar]
|
||||
custom_meshtastic_hw_model = 108
|
||||
|
||||
@@ -47,7 +47,7 @@ build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/meshlin
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/55f618961db45a23eff0233546430f1e5a80f63a.zip
|
||||
debug_tool = jlink
|
||||
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
|
||||
; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds
|
||||
|
||||
@@ -36,7 +36,7 @@ lib_deps =
|
||||
# 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
|
||||
# renovate: datasource=custom.pio depName=ArduinoJson packageName=bblanchon/library/ArduinoJson
|
||||
bblanchon/ArduinoJson@6.21.5
|
||||
bblanchon/ArduinoJson@6.21.4
|
||||
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
|
||||
; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds
|
||||
;upload_protocol = jlink
|
||||
|
||||
@@ -34,7 +34,7 @@ build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/seeed_w
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/b202ebfec6a4821e098cf7a625ba0f6f2400292d.zip
|
||||
debug_tool = jlink
|
||||
|
||||
[env:seeed_wio_tracker_L1_eink-inkhud]
|
||||
|
||||
@@ -30,7 +30,7 @@ build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/t-echo>
|
||||
lib_deps =
|
||||
${nrf52840_base.lib_deps}
|
||||
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
|
||||
https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
|
||||
https://github.com/meshtastic/GxEPD2/archive/55f618961db45a23eff0233546430f1e5a80f63a.zip
|
||||
# renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
|
||||
lewisxhe/SensorLib@0.3.3
|
||||
;upload_protocol = fs
|
||||
|
||||
Reference in New Issue
Block a user