Compare commits
25
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e30795c6b | ||
|
|
0228d4e2df | ||
|
|
11963ea3d2 | ||
|
|
aeb1e7e71a | ||
|
|
aecdc93c98 | ||
|
|
fa28412b82 | ||
|
|
cf5ecf30d7 | ||
|
|
df3934de85 | ||
|
|
c911a8e3f1 | ||
|
|
f392487b46 | ||
|
|
a8c2fb3945 | ||
|
|
c0d108adff | ||
|
|
f7ccab95b2 | ||
|
|
dc08311b56 | ||
|
|
ce389e44cd | ||
|
|
a19d88b022 | ||
|
|
8757584f93 | ||
|
|
83a49536c0 | ||
|
|
49c47c24e3 | ||
|
|
d847061d02 | ||
|
|
451695a210 | ||
|
|
20cd4a196c | ||
|
|
8575e13da8 | ||
|
|
91588f5136 | ||
|
|
82325a60ba |
@@ -1,314 +0,0 @@
|
||||
# Meshtastic Firmware - Copilot Instructions
|
||||
|
||||
This document provides context and guidelines for AI assistants working with the Meshtastic firmware codebase.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Meshtastic is an open-source LoRa mesh networking project for long-range, low-power communication without relying on internet or cellular infrastructure. The firmware enables text messaging, location sharing, and telemetry over a decentralized mesh network.
|
||||
|
||||
### Supported Hardware Platforms
|
||||
|
||||
- **ESP32** (ESP32, ESP32-S3, ESP32-C3) - Most common platform
|
||||
- **nRF52** (nRF52840, nRF52833) - Low power Nordic chips
|
||||
- **RP2040/RP2350** - Raspberry Pi Pico variants
|
||||
- **STM32WL** - STM32 with integrated LoRa
|
||||
- **Linux/Portduino** - Native Linux builds (Raspberry Pi, etc.)
|
||||
|
||||
### Supported Radio Chips
|
||||
|
||||
- **SX1262/SX1268** - Sub-GHz LoRa (868/915 MHz regions)
|
||||
- **SX1280** - 2.4 GHz LoRa
|
||||
- **LR1110/LR1120/LR1121** - Wideband radios (sub-GHz and 2.4 GHz capable, but not simultaneously)
|
||||
- **RF95** - Legacy RFM95 modules
|
||||
- **LLCC68** - Low-cost LoRa
|
||||
|
||||
### MQTT Integration
|
||||
|
||||
MQTT provides a bridge between Meshtastic mesh networks and the internet, enabling nodes with network connectivity to share messages with remote meshes or external services.
|
||||
|
||||
#### Key Components
|
||||
|
||||
- **`src/mqtt/MQTT.cpp`** - Main MQTT client singleton, handles connection and message routing
|
||||
- **`src/mqtt/ServiceEnvelope.cpp`** - Protobuf wrapper for mesh packets sent over MQTT
|
||||
- **`moduleConfig.mqtt`** - MQTT module configuration
|
||||
|
||||
#### MQTT Topic Structure
|
||||
|
||||
Messages are published/subscribed using a hierarchical topic format:
|
||||
|
||||
```
|
||||
{root}/{channel_id}/{gateway_id}
|
||||
```
|
||||
|
||||
- `root` - Configurable prefix (default: `msh`)
|
||||
- `channel_id` - Channel name/identifier
|
||||
- `gateway_id` - Node ID of the publishing gateway
|
||||
|
||||
#### Configuration Defaults (from `Default.h`)
|
||||
|
||||
```cpp
|
||||
#define default_mqtt_address "mqtt.meshtastic.org"
|
||||
#define default_mqtt_username "meshdev"
|
||||
#define default_mqtt_password "large4cats"
|
||||
#define default_mqtt_root "msh"
|
||||
#define default_mqtt_encryption_enabled true
|
||||
#define default_mqtt_tls_enabled false
|
||||
```
|
||||
|
||||
#### Key Concepts
|
||||
|
||||
- **Uplink** - Mesh packets sent TO the MQTT broker (controlled by `uplink_enabled` per channel)
|
||||
- **Downlink** - MQTT messages received and injected INTO the mesh (controlled by `downlink_enabled` per channel)
|
||||
- **Encryption** - When `encryption_enabled` is true, only encrypted packets are sent; plaintext JSON is disabled
|
||||
- **ServiceEnvelope** - Protobuf wrapper containing packet + channel_id + gateway_id for routing
|
||||
- **JSON Support** - Optional JSON encoding for integration with external systems (disabled on nRF52 by default)
|
||||
|
||||
#### PKI Messages
|
||||
|
||||
PKI (Public Key Infrastructure) messages have special handling:
|
||||
|
||||
- Accepted on a special "PKI" channel
|
||||
- Allow encrypted DMs between nodes that discovered each other on downlink-enabled channels
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
firmware/
|
||||
├── src/ # Main source code
|
||||
│ ├── main.cpp # Application entry point
|
||||
│ ├── mesh/ # Core mesh networking
|
||||
│ │ ├── NodeDB.* # Node database management
|
||||
│ │ ├── Router.* # Packet routing
|
||||
│ │ ├── Channels.* # Channel management
|
||||
│ │ ├── *Interface.* # Radio interface implementations
|
||||
│ │ └── generated/ # Protobuf generated code
|
||||
│ ├── modules/ # Feature modules (Position, Telemetry, etc.)
|
||||
│ ├── gps/ # GPS handling
|
||||
│ ├── graphics/ # Display drivers and UI
|
||||
│ ├── platform/ # Platform-specific code
|
||||
│ ├── input/ # Input device handling
|
||||
│ └── concurrency/ # Threading utilities
|
||||
├── variants/ # Hardware variant definitions
|
||||
│ ├── esp32/ # ESP32 variants
|
||||
│ ├── esp32s3/ # ESP32-S3 variants
|
||||
│ ├── nrf52/ # nRF52 variants
|
||||
│ └── rp2xxx/ # RP2040/RP2350 variants
|
||||
├── protobufs/ # Protocol buffer definitions
|
||||
├── boards/ # Custom PlatformIO board definitions
|
||||
└── bin/ # Build and utility scripts
|
||||
```
|
||||
|
||||
## Coding Conventions
|
||||
|
||||
### General Style
|
||||
|
||||
- Follow existing code style - run `trunk fmt` before commits
|
||||
- Prefer `LOG_DEBUG`, `LOG_INFO`, `LOG_WARN`, `LOG_ERROR` for logging
|
||||
- Use `assert()` for invariants that should never fail
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
- Classes: `PascalCase` (e.g., `PositionModule`, `NodeDB`)
|
||||
- Functions/Methods: `camelCase` (e.g., `sendOurPosition`, `getNodeNum`)
|
||||
- Constants/Defines: `UPPER_SNAKE_CASE` (e.g., `MAX_INTERVAL`, `ONE_DAY`)
|
||||
- Member variables: `camelCase` (e.g., `lastGpsSend`, `nodeDB`)
|
||||
- Config defines: `USERPREFS_*` for user-configurable options
|
||||
|
||||
### Key Patterns
|
||||
|
||||
#### Module System
|
||||
|
||||
Modules inherit from `MeshModule` or `ProtobufModule<T>` and implement:
|
||||
|
||||
- `handleReceivedProtobuf()` - Process incoming packets
|
||||
- `allocReply()` - Generate response packets
|
||||
- `runOnce()` - Periodic task execution (returns next run interval in ms)
|
||||
|
||||
```cpp
|
||||
class MyModule : public ProtobufModule<meshtastic_MyMessage>
|
||||
{
|
||||
protected:
|
||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_MyMessage *msg) override;
|
||||
virtual int32_t runOnce() override;
|
||||
};
|
||||
```
|
||||
|
||||
#### Configuration Access
|
||||
|
||||
- `config.*` - Device configuration (LoRa, position, power, etc.)
|
||||
- `moduleConfig.*` - Module-specific configuration
|
||||
- `channels.*` - Channel configuration and management
|
||||
|
||||
#### Default Values
|
||||
|
||||
Use the `Default` class helpers in `src/mesh/Default.h`:
|
||||
|
||||
- `Default::getConfiguredOrDefaultMs(configured, default)` - Returns ms, using default if configured is 0
|
||||
- `Default::getConfiguredOrMinimumValue(configured, min)` - Enforces minimum values
|
||||
- `Default::getConfiguredOrDefaultMsScaled(configured, default, numNodes)` - Scales based on network size
|
||||
|
||||
#### Thread Safety
|
||||
|
||||
- Use `concurrency::Lock` for mutex protection
|
||||
- Radio SPI access uses `SPILock`
|
||||
- Prefer `OSThread` for background tasks
|
||||
|
||||
### Hardware Variants
|
||||
|
||||
Each hardware variant has:
|
||||
|
||||
- `variant.h` - Pin definitions and hardware capabilities
|
||||
- `platformio.ini` - Build configuration
|
||||
- Optional: `pins_arduino.h`, `rfswitch.h`
|
||||
|
||||
Key defines in variant.h:
|
||||
|
||||
```cpp
|
||||
#define USE_SX1262 // Radio chip selection
|
||||
#define HAS_GPS 1 // Hardware capabilities
|
||||
#define LORA_CS 36 // Pin assignments
|
||||
#define SX126X_DIO1 14 // Radio-specific pins
|
||||
```
|
||||
|
||||
### Protobuf Messages
|
||||
|
||||
- Defined in `protobufs/meshtastic/*.proto`
|
||||
- Generated code in `src/mesh/generated/`
|
||||
- Regenerate with `bin/regen-protos.sh`
|
||||
- Message types prefixed with `meshtastic_`
|
||||
|
||||
### Conditional Compilation
|
||||
|
||||
```cpp
|
||||
#if !MESHTASTIC_EXCLUDE_GPS // Feature exclusion
|
||||
#ifdef ARCH_ESP32 // Architecture-specific
|
||||
#if defined(USE_SX1262) // Radio-specific
|
||||
#ifdef HAS_SCREEN // Hardware capability
|
||||
#if USERPREFS_EVENT_MODE // User preferences
|
||||
```
|
||||
|
||||
## Build System
|
||||
|
||||
Uses **PlatformIO** with custom scripts:
|
||||
|
||||
- `bin/platformio-pre.py` - Pre-build script
|
||||
- `bin/platformio-custom.py` - Custom build logic
|
||||
|
||||
Build commands:
|
||||
|
||||
```bash
|
||||
pio run -e tbeam # Build specific target
|
||||
pio run -e tbeam -t upload # Build and upload
|
||||
pio run -e native # Build native/Linux version
|
||||
```
|
||||
|
||||
## Common Tasks
|
||||
|
||||
### Adding a New Module
|
||||
|
||||
1. Create `src/modules/MyModule.cpp` and `.h`
|
||||
2. Inherit from appropriate base class
|
||||
3. Register in `src/modules/Modules.cpp`
|
||||
4. Add protobuf messages if needed in `protobufs/`
|
||||
|
||||
### Adding a New Hardware Variant
|
||||
|
||||
1. Create directory under `variants/<arch>/<name>/`
|
||||
2. Add `variant.h` with pin definitions
|
||||
3. Add `platformio.ini` with build config
|
||||
4. Reference common configs with `extends`
|
||||
|
||||
### Modifying Configuration Defaults
|
||||
|
||||
- Check `src/mesh/Default.h` for default value defines
|
||||
- Check `src/mesh/NodeDB.cpp` for initialization logic
|
||||
- Consider `isDefaultChannel()` checks for public channel restrictions
|
||||
|
||||
## Important Considerations
|
||||
|
||||
### Traffic Management
|
||||
|
||||
The mesh network has limited bandwidth. When modifying broadcast intervals:
|
||||
|
||||
- Respect minimum intervals on default/public channels
|
||||
- Use `Default::getConfiguredOrMinimumValue()` to enforce minimums
|
||||
- Consider `numOnlineNodes` scaling for congestion control
|
||||
|
||||
### Power Management
|
||||
|
||||
Many devices are battery-powered:
|
||||
|
||||
- Use `IF_ROUTER(routerVal, normalVal)` for role-based defaults
|
||||
- Check `config.power.is_power_saving` for power-saving modes
|
||||
- Implement proper `sleep()` methods in radio interfaces
|
||||
|
||||
### Channel Security
|
||||
|
||||
- `channels.isDefaultChannel(index)` - Check if using default/public settings
|
||||
- Default channels get stricter rate limits to prevent abuse
|
||||
- Private channels may have relaxed limits
|
||||
|
||||
## GitHub Actions CI/CD
|
||||
|
||||
The project uses GitHub Actions extensively for CI/CD. Key workflows are in `.github/workflows/`:
|
||||
|
||||
### Core CI Workflows
|
||||
|
||||
- **`main_matrix.yml`** - Main CI pipeline, runs on push to `master`/`develop` and PRs
|
||||
- Uses `bin/generate_ci_matrix.py` to dynamically generate build targets
|
||||
- Builds all supported hardware variants
|
||||
- PRs build a subset (`--level pr`) for faster feedback
|
||||
|
||||
- **`trunk_check.yml`** - Code quality checks on PRs
|
||||
- Runs Trunk.io for linting and formatting
|
||||
- Must pass before merge
|
||||
|
||||
- **`tests.yml`** - End-to-end and hardware tests
|
||||
- Runs daily on schedule
|
||||
- Includes native tests and hardware-in-the-loop testing
|
||||
|
||||
- **`test_native.yml`** - Native platform unit tests
|
||||
- Runs `pio test -e native`
|
||||
|
||||
### Release Workflows
|
||||
|
||||
- **`release_channels.yml`** - Triggered on GitHub release publish
|
||||
- Builds Docker images
|
||||
- Packages for PPA (Ubuntu), OBS (openSUSE), and COPR (Fedora)
|
||||
- Handles Alpha/Beta/Stable release channels
|
||||
|
||||
- **`nightly.yml`** - Nightly builds from develop branch
|
||||
|
||||
- **`docker_build.yml`** / **`docker_manifest.yml`** - Docker image builds
|
||||
|
||||
### Build Matrix Generation
|
||||
|
||||
The CI uses `bin/generate_ci_matrix.py` to dynamically select which targets to build:
|
||||
|
||||
```bash
|
||||
# Generate full build matrix
|
||||
./bin/generate_ci_matrix.py all
|
||||
|
||||
# Generate PR-level matrix (subset for faster builds)
|
||||
./bin/generate_ci_matrix.py all --level pr
|
||||
```
|
||||
|
||||
Variants can specify their support level in `platformio.ini`:
|
||||
|
||||
- `custom_meshtastic_support_level = 1` - Actively supported, built on every PR
|
||||
- `custom_meshtastic_support_level = 2` - Supported, built on merge to main branches
|
||||
- `board_level = extra` - Extra builds, only on full releases
|
||||
|
||||
### Running Workflows Locally
|
||||
|
||||
Most workflows can be triggered manually via `workflow_dispatch` for testing.
|
||||
|
||||
## Testing
|
||||
|
||||
- Unit tests in `test/` directory
|
||||
- Run with `pio test -e native`
|
||||
- Use `bin/test-simulator.sh` for simulation testing
|
||||
|
||||
## Resources
|
||||
|
||||
- [Documentation](https://meshtastic.org/docs/)
|
||||
@@ -1,47 +0,0 @@
|
||||
name: Welcome First-Time Contributor
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: opened
|
||||
pull_request_target:
|
||||
types: opened
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
welcome:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write # Required to post comments and labels on issues
|
||||
pull-requests: write # Required to post comments and labels on PRs
|
||||
steps:
|
||||
- uses: plbstl/first-contribution@v4
|
||||
with:
|
||||
labels: first-contribution
|
||||
issue-opened-msg: |
|
||||
### @{fc-author}, Welcome to Meshtastic! :wave:
|
||||
|
||||
Thanks for opening your first issue. If it's helpful, an easy way
|
||||
to get logs is the "Open Serial Monitor" button on the [Web Flasher](https://flasher.meshtastic.org).
|
||||
|
||||
If you have ideas for features, note that we often debate big ideas
|
||||
in the [discussions tab](https://github.com/meshtastic/firmware/discussions/categories/ideas)
|
||||
first. This tracker tends to be for ideas that have community
|
||||
consensus and a clear implementation.
|
||||
|
||||
We're very active [on discord](https://discord.com/invite/meshtastic),
|
||||
especially the \#firmware and \#alphanauts-testing channels. If you'll
|
||||
be around for a while, we'd love to see you there!
|
||||
|
||||
Welcome to the community! :heart:
|
||||
|
||||
pr-opened-msg: |
|
||||
### @{fc-author}, Welcome to Meshtastic!
|
||||
|
||||
Thanks for opening your first pull request. We really appreciate it.
|
||||
|
||||
We discuss work as a team in discord, please join us in the [#firmware channel](https://discord.com/invite/meshtastic).
|
||||
There's a big backlog of patches at the moment. If you have time,
|
||||
please help us with some code review and testing of [other PRs](https://github.com/meshtastic/firmware/pulls)!
|
||||
|
||||
Welcome to the team :smile:
|
||||
@@ -8,9 +8,7 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- pioarduino # Remove when merged // use `feature/` in the future.
|
||||
- event/*
|
||||
- feature/*
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- version.properties
|
||||
@@ -20,9 +18,7 @@ on:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- pioarduino # Remove when merged // use `feature/` in the future.
|
||||
- event/*
|
||||
- feature/*
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
#- "**.yml"
|
||||
@@ -244,7 +240,6 @@ jobs:
|
||||
needs: [build]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
if: github.event_name == 'pull_request_target'
|
||||
with:
|
||||
filter: blob:none # means we download all the git history but none of the commit (except ones with checkout like the head)
|
||||
fetch-depth: 0
|
||||
@@ -258,26 +253,19 @@ jobs:
|
||||
uses: actions/upload-artifact@v6
|
||||
id: upload-manifest
|
||||
with:
|
||||
name: manifests-${{ github.sha }}
|
||||
name: manifests-all
|
||||
overwrite: true
|
||||
path: manifests-new/*.mt.json
|
||||
path: |
|
||||
manifests-new/*.mt.json
|
||||
- name: Find the merge base
|
||||
if: github.event_name == 'pull_request_target'
|
||||
run: echo "MERGE_BASE=$(git merge-base "origin/$base" "$head")" >> $GITHUB_ENV
|
||||
env:
|
||||
base: ${{ github.base_ref }}
|
||||
head: ${{ github.sha }}
|
||||
# Currently broken (for-loop through EVERY artifact -- rate limiting)
|
||||
# - name: Download the old manifests
|
||||
# if: github.event_name == 'pull_request_target'
|
||||
# run: gh run download -R "$repo" --name "manifests-$merge_base" --dir manifest-old/
|
||||
# env:
|
||||
# GH_TOKEN: ${{ github.token }}
|
||||
# merge_base: ${{ env.MERGE_BASE }}
|
||||
# repo: ${{ github.repository }}
|
||||
# - name: Do scan and post comment
|
||||
# if: github.event_name == 'pull_request_target'
|
||||
# run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/
|
||||
head: ${{ github.head_ref }}
|
||||
- name: Download the old manifests
|
||||
run: gh run download -R ${{ github.repository }} --commit ${{ env.MERGE_BASE }} --name manifests-all --dir manifest-old/
|
||||
- name: Do scan and post comment
|
||||
run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/
|
||||
|
||||
release-artifacts:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -143,7 +143,7 @@ jobs:
|
||||
merge-multiple: true
|
||||
|
||||
- name: Test Report
|
||||
uses: dorny/test-reporter@v2.5.0
|
||||
uses: dorny/test-reporter@v2.3.0
|
||||
with:
|
||||
name: PlatformIO Tests
|
||||
path: testreport.xml
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
name: Run Trunk Fmt on PR Comment
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
trunk-fmt:
|
||||
if: github.event.issue.pull_request != null && contains(github.event.comment.body, 'trunk fmt')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{github.event.pull_request.head.ref}}
|
||||
repository: ${{github.event.pull_request.head.repo.full_name}}
|
||||
|
||||
- name: Install trunk
|
||||
run: curl https://get.trunk.io -fsSL | bash
|
||||
|
||||
- name: Run Trunk Fmt
|
||||
run: trunk fmt
|
||||
|
||||
- name: Get release version string
|
||||
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Commit and push changes
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add .
|
||||
git commit -m "Add firmware version ${{ steps.version.outputs.long }}"
|
||||
git push
|
||||
|
||||
- name: Comment on PR
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
github.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: '`trunk fmt` has been run on this PR.'
|
||||
})
|
||||
@@ -41,12 +41,3 @@ src/mesh/raspihttp/private_key.pem
|
||||
|
||||
# Ignore logo (set at build time with platformio-custom.py)
|
||||
data/boot/logo.*
|
||||
|
||||
# pioarduino platform
|
||||
managed_components/*
|
||||
arduino-lib-builder*
|
||||
dependencies.lock
|
||||
idf_component.yml
|
||||
CMakeLists.txt
|
||||
sdkconfig.*
|
||||
.dummy/*
|
||||
|
||||
+5
-5
@@ -8,20 +8,20 @@ plugins:
|
||||
uri: https://github.com/trunk-io/plugins
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.497
|
||||
- renovate@42.75.0
|
||||
- checkov@3.2.495
|
||||
- renovate@42.64.1
|
||||
- prettier@3.7.4
|
||||
- trufflehog@3.92.4
|
||||
- trufflehog@3.92.3
|
||||
- yamllint@1.37.1
|
||||
- bandit@1.9.2
|
||||
- trivy@0.68.2
|
||||
- taplo@0.10.0
|
||||
- ruff@0.14.11
|
||||
- ruff@0.14.10
|
||||
- isort@7.0.0
|
||||
- markdownlint@0.47.0
|
||||
- oxipng@10.0.0
|
||||
- svgo@4.0.0
|
||||
- actionlint@1.7.10
|
||||
- actionlint@1.7.9
|
||||
- flake8@7.3.0
|
||||
- hadolint@2.14.0
|
||||
- shfmt@3.6.0
|
||||
|
||||
+2
-2
@@ -4,8 +4,8 @@
|
||||
|
||||
| Firmware Version | Supported |
|
||||
| ---------------- | ------------------ |
|
||||
| 2.7.x | :white_check_mark: |
|
||||
| <= 2.6.x | :x: |
|
||||
| 2.6.x | :white_check_mark: |
|
||||
| <= 2.5.x | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
+1
-1
@@ -38,4 +38,4 @@ cp bin/device-install.* $OUTDIR/
|
||||
cp bin/device-update.* $OUTDIR/
|
||||
|
||||
echo "Copying manifest"
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json || true
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||
|
||||
+2
-3
@@ -21,14 +21,13 @@ rm -f $BUILDDIR/firmware*
|
||||
export APP_VERSION=$VERSION
|
||||
|
||||
basename=firmware-$1-$VERSION
|
||||
ota_basename=${basename}-ota
|
||||
|
||||
pio run --environment $1 -t mtjson # -v
|
||||
|
||||
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
|
||||
|
||||
echo "Copying NRF52 dfu (OTA) file"
|
||||
cp $BUILDDIR/$basename.zip $OUTDIR/$ota_basename.zip
|
||||
cp $BUILDDIR/$basename.zip $OUTDIR/$basename.zip
|
||||
|
||||
echo "Copying NRF52 UF2 file"
|
||||
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2
|
||||
@@ -49,4 +48,4 @@ if (echo $1 | grep -q "rak4631"); then
|
||||
fi
|
||||
|
||||
echo "Copying manifest"
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json || true
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||
|
||||
+1
-1
@@ -30,4 +30,4 @@ echo "Copying uf2 file"
|
||||
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2
|
||||
|
||||
echo "Copying manifest"
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json || true
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||
|
||||
@@ -30,4 +30,4 @@ echo "Copying STM32 bin file"
|
||||
cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin
|
||||
|
||||
echo "Copying manifest"
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json || true
|
||||
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
Lora:
|
||||
|
||||
### RAK13300in Slot 1
|
||||
Module: sx1262
|
||||
IRQ: 22 #IO6
|
||||
Reset: 16 # IO4
|
||||
Busy: 24 # IO5
|
||||
# Ant_sw: 13 # IO3
|
||||
DIO3_TCXO_VOLTAGE: true
|
||||
DIO2_AS_RF_SWITCH: true
|
||||
spidev: spidev0.0
|
||||
@@ -1,14 +0,0 @@
|
||||
Lora:
|
||||
Module: sx1262
|
||||
CS: 0
|
||||
IRQ: 6
|
||||
Reset: 2
|
||||
Busy: 4
|
||||
RXen: 1
|
||||
DIO2_AS_RF_SWITCH: true
|
||||
spidev: ch341
|
||||
DIO3_TCXO_VOLTAGE: true
|
||||
# USB_Serialnum: 12345678
|
||||
USB_PID: 0x5512
|
||||
USB_VID: 0x1A86
|
||||
SX126X_MAX_POWER: 22
|
||||
@@ -87,7 +87,7 @@
|
||||
</screenshots>
|
||||
|
||||
<releases>
|
||||
<release version="2.7.18" date="2026-01-02">
|
||||
<release version="2.7.18" date="2025-12-20">
|
||||
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.18</url>
|
||||
</release>
|
||||
<release version="2.7.17" date="2025-11-28">
|
||||
|
||||
+14
-149
@@ -2,12 +2,11 @@
|
||||
# trunk-ignore-all(ruff/F821)
|
||||
# trunk-ignore-all(flake8/F821): For SConstruct imports
|
||||
import sys
|
||||
from os.path import join
|
||||
from os.path import join, basename, isfile
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Dict
|
||||
|
||||
from readprops import readProps
|
||||
|
||||
@@ -15,50 +14,9 @@ Import("env")
|
||||
platform = env.PioPlatform()
|
||||
progname = env.get("PROGNAME")
|
||||
lfsbin = f"{progname.replace('firmware-', 'littlefs-')}.bin"
|
||||
manifest_ran = False
|
||||
|
||||
def infer_architecture(board_cfg):
|
||||
try:
|
||||
mcu = board_cfg.get("build.mcu") if board_cfg else None
|
||||
except KeyError:
|
||||
mcu = None
|
||||
except Exception:
|
||||
mcu = None
|
||||
if not mcu:
|
||||
return None
|
||||
mcu_l = str(mcu).lower()
|
||||
if "esp32s3" in mcu_l:
|
||||
return "esp32-s3"
|
||||
if "esp32c6" in mcu_l:
|
||||
return "esp32-c6"
|
||||
if "esp32c3" in mcu_l:
|
||||
return "esp32-c3"
|
||||
if "esp32" in mcu_l:
|
||||
return "esp32"
|
||||
if "rp2040" in mcu_l:
|
||||
return "rp2040"
|
||||
if "rp2350" in mcu_l:
|
||||
return "rp2350"
|
||||
if "nrf52" in mcu_l or "nrf52840" in mcu_l:
|
||||
return "nrf52840"
|
||||
if "stm32" in mcu_l:
|
||||
return "stm32"
|
||||
return None
|
||||
|
||||
def manifest_gather(source, target, env):
|
||||
global manifest_ran
|
||||
if manifest_ran:
|
||||
return
|
||||
# Skip manifest generation if we cannot determine architecture (host/native builds)
|
||||
board_arch = infer_architecture(env.BoardConfig())
|
||||
if not board_arch:
|
||||
print(f"Skipping mtjson generation for unknown architecture (env={env.get('PIOENV')})")
|
||||
manifest_ran = True
|
||||
return
|
||||
manifest_ran = True
|
||||
out = []
|
||||
board_platform = env.BoardConfig().get("platform")
|
||||
needs_ota_suffix = board_platform == "nordicnrf52"
|
||||
check_paths = [
|
||||
progname,
|
||||
f"{progname}.elf",
|
||||
@@ -74,11 +32,8 @@ def manifest_gather(source, target, env):
|
||||
for p in check_paths:
|
||||
f = env.File(env.subst(f"$BUILD_DIR/{p}"))
|
||||
if f.exists():
|
||||
manifest_name = p
|
||||
if needs_ota_suffix and p == f"{progname}.zip":
|
||||
manifest_name = f"{progname}-ota.zip"
|
||||
d = {
|
||||
"name": manifest_name,
|
||||
"name": p,
|
||||
"md5": f.get_content_hash(), # Returns MD5 hash
|
||||
"bytes": f.get_size() # Returns file size in bytes
|
||||
}
|
||||
@@ -87,39 +42,14 @@ def manifest_gather(source, target, env):
|
||||
manifest_write(out, env)
|
||||
|
||||
def manifest_write(files, env):
|
||||
# Defensive: also skip manifest writing if we cannot determine architecture
|
||||
def get_project_option(name):
|
||||
try:
|
||||
return env.GetProjectOption(name)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def get_project_option_any(names):
|
||||
for name in names:
|
||||
val = get_project_option(name)
|
||||
if val is not None:
|
||||
return val
|
||||
return None
|
||||
|
||||
def as_bool(val):
|
||||
return str(val).strip().lower() in ("1", "true", "yes", "on")
|
||||
|
||||
def as_int(val):
|
||||
try:
|
||||
return int(str(val), 10)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
def as_list(val):
|
||||
return [item.strip() for item in str(val).split(",") if item.strip()]
|
||||
|
||||
manifest = {
|
||||
"version": verObj["long"],
|
||||
"build_epoch": build_epoch,
|
||||
"platformioTarget": env.get("PIOENV"),
|
||||
"board": env.get("PIOENV"),
|
||||
"mcu": env.get("BOARD_MCU"),
|
||||
"repo": repo_owner,
|
||||
"files": files,
|
||||
"part": None,
|
||||
"has_mui": False,
|
||||
"has_inkhud": False,
|
||||
}
|
||||
@@ -134,51 +64,6 @@ def manifest_write(files, env):
|
||||
if "MESHTASTIC_INCLUDE_INKHUD" in env.get("CPPDEFINES", []):
|
||||
manifest["has_inkhud"] = True
|
||||
|
||||
pioenv = env.get("PIOENV")
|
||||
device_meta = {}
|
||||
device_meta_fields = [
|
||||
("hwModel", ["custom_meshtastic_hw_model"], as_int),
|
||||
("hwModelSlug", ["custom_meshtastic_hw_model_slug"], str),
|
||||
("architecture", ["custom_meshtastic_architecture"], str),
|
||||
("activelySupported", ["custom_meshtastic_actively_supported"], as_bool),
|
||||
("displayName", ["custom_meshtastic_display_name"], str),
|
||||
("supportLevel", ["custom_meshtastic_support_level"], as_int),
|
||||
("images", ["custom_meshtastic_images"], as_list),
|
||||
("tags", ["custom_meshtastic_tags"], as_list),
|
||||
("requiresDfu", ["custom_meshtastic_requires_dfu"], as_bool),
|
||||
("partitionScheme", ["custom_meshtastic_partition_scheme"], str),
|
||||
("url", ["custom_meshtastic_url"], str),
|
||||
("key", ["custom_meshtastic_key"], str),
|
||||
("variant", ["custom_meshtastic_variant"], str),
|
||||
]
|
||||
|
||||
|
||||
for manifest_key, option_keys, caster in device_meta_fields:
|
||||
raw_val = get_project_option_any(option_keys)
|
||||
if raw_val is None:
|
||||
continue
|
||||
parsed = caster(raw_val) if callable(caster) else raw_val
|
||||
if parsed is not None and parsed != "":
|
||||
device_meta[manifest_key] = parsed
|
||||
|
||||
# Determine architecture once; if we can't infer it, skip manifest generation
|
||||
board_arch = device_meta.get("architecture") or infer_architecture(env.BoardConfig())
|
||||
if not board_arch:
|
||||
print(f"Skipping mtjson write for unknown architecture (env={env.get('PIOENV')})")
|
||||
return
|
||||
|
||||
device_meta["architecture"] = board_arch
|
||||
|
||||
# Always set requiresDfu: true for nrf52840 targets
|
||||
if board_arch == "nrf52840":
|
||||
device_meta["requiresDfu"] = True
|
||||
|
||||
device_meta.setdefault("displayName", pioenv)
|
||||
device_meta.setdefault("activelySupported", False)
|
||||
|
||||
if device_meta:
|
||||
manifest.update(device_meta)
|
||||
|
||||
# Write the manifest to the build directory
|
||||
with open(env.subst("$BUILD_DIR/${PROGNAME}.mt.json"), "w") as f:
|
||||
json.dump(manifest, f, indent=2)
|
||||
@@ -276,12 +161,8 @@ def load_boot_logo(source, target, env):
|
||||
if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
|
||||
env.AddPreAction(f"$BUILD_DIR/{lfsbin}", load_boot_logo)
|
||||
|
||||
board_arch = infer_architecture(env.BoardConfig())
|
||||
should_skip_manifest = board_arch is None
|
||||
|
||||
# For host/native envs, avoid depending on 'buildprog' (some targets don't define it)
|
||||
mtjson_deps = [] if should_skip_manifest else ["buildprog"]
|
||||
if not should_skip_manifest and platform.name == "espressif32":
|
||||
mtjson_deps = ["buildprog"]
|
||||
if platform.name == "espressif32":
|
||||
# Build littlefs image as part of mtjson target
|
||||
# Equivalent to `pio run -t buildfs`
|
||||
target_lfs = env.DataToBin(
|
||||
@@ -289,27 +170,11 @@ if not should_skip_manifest and platform.name == "espressif32":
|
||||
)
|
||||
mtjson_deps.append(target_lfs)
|
||||
|
||||
if should_skip_manifest:
|
||||
def skip_manifest(source, target, env):
|
||||
print(f"mtjson: skipped for native environment: {env.get('PIOENV')}")
|
||||
|
||||
env.AddCustomTarget(
|
||||
name="mtjson",
|
||||
dependencies=mtjson_deps,
|
||||
actions=[skip_manifest],
|
||||
title="Meshtastic Manifest (skipped)",
|
||||
description="mtjson generation is skipped for native environments",
|
||||
always_build=True,
|
||||
)
|
||||
else:
|
||||
env.AddCustomTarget(
|
||||
name="mtjson",
|
||||
dependencies=mtjson_deps,
|
||||
actions=[manifest_gather],
|
||||
title="Meshtastic Manifest",
|
||||
description="Generating Meshtastic manifest JSON + Checksums",
|
||||
always_build=True,
|
||||
)
|
||||
|
||||
# Run manifest generation as part of the default build pipeline for non-native builds.
|
||||
env.Default("mtjson")
|
||||
env.AddCustomTarget(
|
||||
name="mtjson",
|
||||
dependencies=mtjson_deps,
|
||||
actions=[manifest_gather],
|
||||
title="Meshtastic Manifest",
|
||||
description="Generating Meshtastic manifest JSON + Checksums",
|
||||
always_build=False,
|
||||
)
|
||||
|
||||
+1
-2
@@ -18,9 +18,8 @@ def readProps(prefsLoc):
|
||||
|
||||
# Try to find current build SHA if if the workspace is clean. This could fail if git is not installed
|
||||
try:
|
||||
# Pin abbreviation length to keep local builds and CI matching (avoid auto-shortening)
|
||||
sha = (
|
||||
subprocess.check_output(["git", "rev-parse", "--short=7", "HEAD"])
|
||||
subprocess.check_output(["git", "rev-parse", "--short", "HEAD"])
|
||||
.decode("utf-8")
|
||||
.strip()
|
||||
)
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DLILYGO_TBEAM_1W",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=0",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"psram_type": "opi",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "t-beam-1w"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "LilyGo TBeam-1W",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 16777216,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "http://www.lilygo.cn/",
|
||||
"vendor": "LilyGo"
|
||||
}
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@ meshtasticd (2.7.18.0) unstable; urgency=medium
|
||||
|
||||
* Version 2.7.18
|
||||
|
||||
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Fri, 02 Jan 2026 12:45:36 +0000
|
||||
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Sat, 20 Dec 2025 15:47:25 +0000
|
||||
|
||||
meshtasticd (2.7.17.0) unstable; urgency=medium
|
||||
|
||||
|
||||
+3
-3
@@ -64,7 +64,7 @@ monitor_speed = 115200
|
||||
monitor_filters = direct
|
||||
lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/b34c6817c25d6faabb3a8a162b5d14fb75395433.zip
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/2887bf4a19f64d92c984dcc8fd5ca7429e425e4a.zip
|
||||
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
|
||||
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
|
||||
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
|
||||
@@ -94,7 +94,7 @@ lib_deps =
|
||||
# renovate: datasource=custom.pio depName=NonBlockingRTTTL packageName=end2endzone/library/NonBlockingRTTTL
|
||||
end2endzone/NonBlockingRTTTL@1.4.0
|
||||
build_flags = ${env.build_flags} -Os
|
||||
build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/>
|
||||
build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/> -<modules/Native/>
|
||||
|
||||
; Common libs for communicating over TCP/IP networks such as MQTT
|
||||
[networking_base]
|
||||
@@ -119,7 +119,7 @@ lib_deps =
|
||||
[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/272defcb35651461830ebfd1b39c9167c8f49317.zip
|
||||
https://github.com/meshtastic/device-ui/archive/862ed040c4ab44f0dfbbe492691f144886102588.zip
|
||||
|
||||
; Common libs for environmental measurements in telemetry module
|
||||
[environmental_base]
|
||||
|
||||
+1
-1
Submodule protobufs updated: f78b3f0dcc...c474fd3f49
+3
-3
@@ -1149,11 +1149,11 @@ bool Power::axpChipInit()
|
||||
PMU->setPowerChannelVoltage(XPOWERS_ALDO1, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_ALDO1);
|
||||
|
||||
// sdcard (T-Beam S3) / gnns (T-Watch S3 Plus) power channel
|
||||
// sdcard power channel
|
||||
PMU->setPowerChannelVoltage(XPOWERS_BLDO1, 3300);
|
||||
#ifndef T_WATCH_S3
|
||||
PMU->enablePowerOutput(XPOWERS_BLDO1);
|
||||
#else
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
// DRV2605 power channel
|
||||
PMU->setPowerChannelVoltage(XPOWERS_BLDO2, 3300);
|
||||
PMU->enablePowerOutput(XPOWERS_BLDO2);
|
||||
|
||||
@@ -22,19 +22,12 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
|
||||
|
||||
// Handle different input events with appropriate buzzer feedback
|
||||
switch (event->inputEvent) {
|
||||
#ifdef INPUTDRIVER_ENCODER_TYPE
|
||||
case INPUT_BROKER_SELECT:
|
||||
case INPUT_BROKER_SELECT_LONG:
|
||||
playClick();
|
||||
break;
|
||||
#else
|
||||
case INPUT_BROKER_USER_PRESS:
|
||||
case INPUT_BROKER_ALT_PRESS:
|
||||
case INPUT_BROKER_SELECT:
|
||||
case INPUT_BROKER_SELECT_LONG:
|
||||
playBeep();
|
||||
playBeep(); // Confirmation feedback
|
||||
break;
|
||||
#endif
|
||||
|
||||
case INPUT_BROKER_UP:
|
||||
case INPUT_BROKER_UP_LONG:
|
||||
@@ -65,4 +58,4 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
|
||||
}
|
||||
|
||||
return 0; // Allow other handlers to process the event
|
||||
}
|
||||
}
|
||||
+2
-31
@@ -35,14 +35,6 @@ struct ToneDuration {
|
||||
#define NOTE_G6 1568
|
||||
#define NOTE_E7 2637
|
||||
|
||||
#define NOTE_C4 262
|
||||
#define NOTE_E4 330
|
||||
#define NOTE_G4 392
|
||||
#define NOTE_A4 440
|
||||
#define NOTE_C5 523
|
||||
#define NOTE_E5 659
|
||||
#define NOTE_G5 784
|
||||
|
||||
const int DURATION_1_16 = 62; // 1/16 note
|
||||
const int DURATION_1_8 = 125; // 1/8 note
|
||||
const int DURATION_1_4 = 250; // 1/4 note
|
||||
@@ -73,7 +65,7 @@ void playTones(const ToneDuration *tone_durations, int size)
|
||||
|
||||
void playBeep()
|
||||
{
|
||||
ToneDuration melody[] = {{NOTE_B3, DURATION_1_16}};
|
||||
ToneDuration melody[] = {{NOTE_B3, DURATION_1_8}};
|
||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||
}
|
||||
|
||||
@@ -121,14 +113,7 @@ void playShutdownMelody()
|
||||
void playChirp()
|
||||
{
|
||||
// A short, friendly "chirp" sound for key presses
|
||||
ToneDuration melody[] = {{NOTE_AS3, 20}}; // Short AS3 note
|
||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||
}
|
||||
|
||||
void playClick()
|
||||
{
|
||||
// A very short "click" sound with minimum delay; ideal for rotary encoder events
|
||||
ToneDuration melody[] = {{NOTE_AS3, 1}}; // Very Short AS3
|
||||
ToneDuration melody[] = {{NOTE_AS3, 20}}; // Very short AS3 note
|
||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||
}
|
||||
|
||||
@@ -197,17 +182,3 @@ void playComboTune()
|
||||
};
|
||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||
}
|
||||
|
||||
void play4ClickDown()
|
||||
{
|
||||
ToneDuration melody[] = {{NOTE_G5, 55}, {NOTE_E5, 55}, {NOTE_C5, 60}, {NOTE_A4, 55}, {NOTE_G4, 55},
|
||||
{NOTE_E4, 65}, {NOTE_C4, 80}, {NOTE_G3, 120}, {NOTE_E3, 160}, {NOTE_SILENT, 120}};
|
||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||
}
|
||||
|
||||
void play4ClickUp()
|
||||
{
|
||||
// Quick high-pitched notes with trills
|
||||
ToneDuration melody[] = {{NOTE_F5, 50}, {NOTE_G6, 45}, {NOTE_E7, 60}};
|
||||
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
||||
}
|
||||
@@ -7,11 +7,8 @@ void playShutdownMelody();
|
||||
void playGPSEnableBeep();
|
||||
void playGPSDisableBeep();
|
||||
void playComboTune();
|
||||
void play4ClickDown();
|
||||
void play4ClickUp();
|
||||
void playBoop();
|
||||
void playChirp();
|
||||
void playClick();
|
||||
void playLongPressLeadUp();
|
||||
bool playNextLeadUpNote(); // Play the next note in the lead-up sequence
|
||||
void resetLeadUpSequence(); // Reset the lead-up sequence to start from beginning
|
||||
@@ -444,18 +444,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// BME680 BSEC2 support detection
|
||||
#if !defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
|
||||
#if defined(RAK_4631) || defined(TBEAM_V10)
|
||||
|
||||
#define MESHTASTIC_BME680_BSEC2_SUPPORTED 1
|
||||
#define MESHTASTIC_BME680_HEADER <bsec2.h>
|
||||
#else
|
||||
#define MESHTASTIC_BME680_BSEC2_SUPPORTED 0
|
||||
#define MESHTASTIC_BME680_HEADER <Adafruit_BME680.h>
|
||||
#endif // defined(RAK_4631)
|
||||
#endif // !defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Global switches to turn off features for a minimized build
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -68,7 +68,7 @@ ScanI2C::DeviceType ScanI2CTwoWire::probeOLED(ScanI2C::DeviceAddress addr) const
|
||||
if (r == 0x08 || r == 0x00) {
|
||||
logFoundDevice("SH1106", (uint8_t)addr.address);
|
||||
o_probe = SCREEN_SH1106; // SH1106
|
||||
} else if (r == 0x03 || r == 0x04 || r == 0x06 || r == 0x07 || r == 0x05) {
|
||||
} else if (r == 0x03 || r == 0x04 || r == 0x06 || r == 0x07) {
|
||||
logFoundDevice("SSD1306", (uint8_t)addr.address);
|
||||
o_probe = SCREEN_SSD1306; // SSD1306
|
||||
}
|
||||
@@ -487,7 +487,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
}
|
||||
break;
|
||||
case TSL25911_ADDR:
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xA0 | 0x12), 1);
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x12), 1);
|
||||
if (registerValue == 0x50) {
|
||||
type = TSL2591;
|
||||
logFoundDevice("TSL25911", (uint8_t)addr.address);
|
||||
|
||||
+1
-4
@@ -934,11 +934,8 @@ void GPS::setPowerPMU(bool on)
|
||||
// t-beam v1.2 GNSS power channel
|
||||
on ? PMU->enablePowerOutput(XPOWERS_ALDO3) : PMU->disablePowerOutput(XPOWERS_ALDO3);
|
||||
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) {
|
||||
// t-beam-s3-core GNSS power channel
|
||||
// t-beam-s3-core GNSS power channel
|
||||
on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4);
|
||||
} else if (HW_VENDOR == meshtastic_HardwareModel_T_WATCH_S3) {
|
||||
// t-watch-s3-plus GNSS power channel
|
||||
on ? PMU->enablePowerOutput(XPOWERS_BLDO1) : PMU->disablePowerOutput(XPOWERS_BLDO1);
|
||||
}
|
||||
} else if (model == XPOWERS_AXP192) {
|
||||
// t-beam v1.1 GNSS power channel
|
||||
|
||||
@@ -148,7 +148,7 @@ bool EInkDisplay::connect()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE) || defined(TTGO_T_ECHO_PLUS)
|
||||
#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE)
|
||||
{
|
||||
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
|
||||
|
||||
|
||||
+2
-18
@@ -312,7 +312,6 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
||||
|
||||
// Only validate the combined value once
|
||||
if (rawRGB > 0 && rawRGB <= 255255255) {
|
||||
LOG_INFO("Setting screen RGB color to user chosen: 0x%06X", rawRGB);
|
||||
// Extract each component as a normal int first
|
||||
int r = (rawRGB >> 16) & 0xFF;
|
||||
int g = (rawRGB >> 8) & 0xFF;
|
||||
@@ -320,16 +319,6 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
||||
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
|
||||
TFT_MESH = COLOR565(static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b));
|
||||
}
|
||||
#ifdef TFT_MESH_OVERRIDE
|
||||
} else if (rawRGB == 0) {
|
||||
LOG_INFO("Setting screen RGB color to TFT_MESH_OVERRIDE: 0x%04X", TFT_MESH_OVERRIDE);
|
||||
// Default to TFT_MESH_OVERRIDE if available
|
||||
TFT_MESH = TFT_MESH_OVERRIDE;
|
||||
#endif
|
||||
} else {
|
||||
// Default best readable yellow color
|
||||
LOG_INFO("Setting screen RGB color to default: (255,255,128)");
|
||||
TFT_MESH = COLOR565(255, 255, 128);
|
||||
}
|
||||
|
||||
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
|
||||
@@ -1440,15 +1429,10 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
}
|
||||
nodeDB->updateGUI = false;
|
||||
break;
|
||||
case STATUS_TYPE_POWER: {
|
||||
bool currentUSB = powerStatus->getHasUSB();
|
||||
if (currentUSB != lastPowerUSBState) {
|
||||
lastPowerUSBState = currentUSB;
|
||||
forceDisplay(true);
|
||||
}
|
||||
case STATUS_TYPE_POWER:
|
||||
forceDisplay(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -715,8 +715,6 @@ class Screen : public concurrency::OSThread
|
||||
// Whether we are showing the regular screen (as opposed to booth screen or
|
||||
// Bluetooth PIN screen)
|
||||
bool showingNormalScreen = false;
|
||||
/// Track USB power state to only wake screen on actual power state changes
|
||||
bool lastPowerUSBState = false;
|
||||
|
||||
// Implementation to Adjust Brightness
|
||||
uint8_t brightness = BRIGHTNESS_DEFAULT; // H = 254, MH = 192, ML = 130 L = 103
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "graphics/draw/UIRenderer.h"
|
||||
#include "main.h"
|
||||
#include "meshtastic/config.pb.h"
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
#include "power.h"
|
||||
#include <OLEDDisplay.h>
|
||||
#include <graphics/images.h>
|
||||
@@ -57,6 +56,7 @@ void decomposeTime(uint32_t rtc_sec, int &hour, int &minute, int &second)
|
||||
|
||||
// === Shared External State ===
|
||||
bool hasUnreadMessage = false;
|
||||
bool isMuted = false;
|
||||
ScreenResolution currentResolution = ScreenResolution::Low;
|
||||
|
||||
// === Internal State ===
|
||||
@@ -306,7 +306,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
||||
}
|
||||
display->drawXbm(iconX, iconY, mail_width, mail_height, mail);
|
||||
}
|
||||
} else if (externalNotificationModule->getMute()) {
|
||||
} else if (isMuted) {
|
||||
if (currentResolution == ScreenResolution::High) {
|
||||
int iconX = iconRightEdge - mute_symbol_big_width;
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2;
|
||||
@@ -325,7 +325,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
||||
int iconX = iconRightEdge - mute_symbol_width;
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
|
||||
|
||||
if (isInverted && !force_no_invert) {
|
||||
if (isInverted) {
|
||||
display->setColor(WHITE);
|
||||
display->fillRect(iconX - 1, iconY - 1, mute_symbol_width + 2, mute_symbol_height + 2);
|
||||
display->setColor(BLACK);
|
||||
@@ -383,7 +383,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - mail_height) / 2;
|
||||
display->drawXbm(iconX, iconY, mail_width, mail_height, mail);
|
||||
}
|
||||
} else if (externalNotificationModule->getMute()) {
|
||||
} else if (isMuted) {
|
||||
if (currentResolution == ScreenResolution::High) {
|
||||
int iconX = iconRightEdge - mute_symbol_big_width;
|
||||
int iconY = textY + (FONT_HEIGHT_SMALL - mute_symbol_big_height) / 2;
|
||||
@@ -470,49 +470,18 @@ bool isAllowedPunctuation(char c)
|
||||
return allowed.find(c) != std::string::npos;
|
||||
}
|
||||
|
||||
static void replaceAll(std::string &s, const std::string &from, const std::string &to)
|
||||
{
|
||||
if (from.empty())
|
||||
return;
|
||||
size_t pos = 0;
|
||||
while ((pos = s.find(from, pos)) != std::string::npos) {
|
||||
s.replace(pos, from.size(), to);
|
||||
pos += to.size();
|
||||
}
|
||||
}
|
||||
|
||||
std::string sanitizeString(const std::string &input)
|
||||
{
|
||||
std::string output;
|
||||
bool inReplacement = false;
|
||||
|
||||
// Make a mutable copy so we can normalize UTF-8 “smart punctuation” into ASCII first.
|
||||
std::string s = input;
|
||||
|
||||
// Curly single quotes: ‘ ’
|
||||
replaceAll(s, "\xE2\x80\x98", "'"); // U+2018
|
||||
replaceAll(s, "\xE2\x80\x99", "'"); // U+2019
|
||||
|
||||
// Curly double quotes: “ ”
|
||||
replaceAll(s, "\xE2\x80\x9C", "\""); // U+201C
|
||||
replaceAll(s, "\xE2\x80\x9D", "\""); // U+201D
|
||||
|
||||
// En dash / Em dash: – —
|
||||
replaceAll(s, "\xE2\x80\x93", "-"); // U+2013
|
||||
replaceAll(s, "\xE2\x80\x94", "-"); // U+2014
|
||||
|
||||
// Non-breaking space
|
||||
replaceAll(s, "\xC2\xA0", " "); // U+00A0
|
||||
|
||||
// Now do your original sanitize pass over the normalized string.
|
||||
for (unsigned char uc : s) {
|
||||
char c = static_cast<char>(uc);
|
||||
if (std::isalnum(uc) || isAllowedPunctuation(c)) {
|
||||
for (char c : input) {
|
||||
if (std::isalnum(static_cast<unsigned char>(c)) || isAllowedPunctuation(c)) {
|
||||
output += c;
|
||||
inReplacement = false;
|
||||
} else {
|
||||
if (!inReplacement) {
|
||||
output += static_cast<char>(0xBF); // ISO-8859-1 for inverted question mark
|
||||
output += 0xbf; // ISO-8859-1 for inverted question mark
|
||||
inReplacement = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace graphics
|
||||
|
||||
// Shared state (declare inside namespace)
|
||||
extern bool hasUnreadMessage;
|
||||
extern bool isMuted;
|
||||
enum class ScreenResolution : uint8_t { UltraLow = 0, Low = 1, High = 2 };
|
||||
extern ScreenResolution currentResolution;
|
||||
ScreenResolution determineScreenResolution(int16_t screenheight, int16_t screenwidth);
|
||||
|
||||
+379
-732
File diff suppressed because it is too large
Load Diff
@@ -22,9 +22,6 @@ class menuHandler
|
||||
node_base_menu,
|
||||
gps_toggle_menu,
|
||||
gps_format_menu,
|
||||
gps_smart_position_menu,
|
||||
gps_update_interval_menu,
|
||||
gps_position_broadcast_menu,
|
||||
compass_point_north_menu,
|
||||
reset_node_db_menu,
|
||||
buzzermodemenupicker,
|
||||
@@ -39,6 +36,7 @@ class menuHandler
|
||||
number_test,
|
||||
wifi_toggle_menu,
|
||||
bluetooth_toggle_menu,
|
||||
notifications_menu,
|
||||
screen_options_menu,
|
||||
power_menu,
|
||||
system_base_menu,
|
||||
@@ -79,9 +77,6 @@ class menuHandler
|
||||
static void compassNorthMenu();
|
||||
static void GPSToggleMenu();
|
||||
static void GPSFormatMenu();
|
||||
static void GPSSmartPositionMenu();
|
||||
static void GPSUpdateIntervalMenu();
|
||||
static void GPSPositionBroadcastMenu();
|
||||
static void BuzzerModeMenu();
|
||||
static void switchToMUIMenu();
|
||||
static void TFTColorPickerMenu(OLEDDisplay *display);
|
||||
@@ -97,6 +92,7 @@ class menuHandler
|
||||
static void numberTest();
|
||||
static void wifiBaseMenu();
|
||||
static void wifiToggleMenu();
|
||||
static void notificationsMenu();
|
||||
static void screenOptionsMenu();
|
||||
static void powerMenu();
|
||||
static void nodeNameLengthMenu();
|
||||
@@ -128,28 +124,7 @@ template <typename T> struct MenuOption {
|
||||
MenuOption(const char *labelIn, OptionsAction actionIn) : label(labelIn), action(actionIn), hasValue(false), value() {}
|
||||
};
|
||||
|
||||
struct ScreenColor {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
bool useVariant;
|
||||
|
||||
ScreenColor(uint8_t rIn = 0, uint8_t gIn = 0, uint8_t bIn = 0, bool variantIn = false)
|
||||
: r(rIn), g(gIn), b(bIn), useVariant(variantIn)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
using RadioPresetOption = MenuOption<meshtastic_Config_LoRaConfig_ModemPreset>;
|
||||
using LoraRegionOption = MenuOption<meshtastic_Config_LoRaConfig_RegionCode>;
|
||||
using TimezoneOption = MenuOption<const char *>;
|
||||
using CompassOption = MenuOption<meshtastic_CompassMode>;
|
||||
using ScreenColorOption = MenuOption<ScreenColor>;
|
||||
using GPSToggleOption = MenuOption<meshtastic_Config_PositionConfig_GpsMode>;
|
||||
using GPSFormatOption = MenuOption<meshtastic_DeviceUIConfig_GpsCoordinateFormat>;
|
||||
using NodeNameOption = MenuOption<bool>;
|
||||
using PositionMenuOption = MenuOption<int>;
|
||||
using ClockFaceOption = MenuOption<bool>;
|
||||
|
||||
} // namespace graphics
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifdef MESHTASTIC_INCLUDE_INKHUD
|
||||
|
||||
#include "./PositionsApplet.h"
|
||||
#include "NodeDB.h"
|
||||
|
||||
using namespace NicheGraphics;
|
||||
|
||||
@@ -50,8 +49,8 @@ ProcessMessage InkHUD::PositionsApplet::handleReceived(const meshtastic_MeshPack
|
||||
if (!hasPosition)
|
||||
return ProcessMessage::CONTINUE;
|
||||
|
||||
const int8_t hopsAway = getHopsAway(mp);
|
||||
const bool hasHopsAway = hopsAway >= 0;
|
||||
bool hasHopsAway = (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start); // From NodeDB::updateFrom
|
||||
uint8_t hopsAway = mp.hop_start - mp.hop_limit;
|
||||
|
||||
// Determine if the position packet would change anything on-screen
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
@@ -37,9 +37,6 @@ bool ButtonThread::initButton(const ButtonConfig &config)
|
||||
_activeLow = config.activeLow;
|
||||
_touchQuirk = config.touchQuirk;
|
||||
_intRoutine = config.intRoutine;
|
||||
_pressHandler = config.onPress;
|
||||
_releaseHandler = config.onRelease;
|
||||
_suppressLeadUp = config.suppressLeadUpSound;
|
||||
_longLongPress = config.longLongPress;
|
||||
|
||||
userButton = OneButton(config.pinNumber, config.activeLow, config.activePullup);
|
||||
@@ -136,8 +133,6 @@ int32_t ButtonThread::runOnce()
|
||||
|
||||
// Detect start of button press
|
||||
if (buttonCurrentlyPressed && !buttonWasPressed) {
|
||||
if (_pressHandler)
|
||||
_pressHandler();
|
||||
buttonPressStartTime = millis();
|
||||
leadUpPlayed = false;
|
||||
leadUpSequenceActive = false;
|
||||
@@ -145,7 +140,7 @@ int32_t ButtonThread::runOnce()
|
||||
}
|
||||
|
||||
// Progressive lead-up sound system
|
||||
if (!_suppressLeadUp && buttonCurrentlyPressed && (millis() - buttonPressStartTime) >= BUTTON_LEADUP_MS) {
|
||||
if (buttonCurrentlyPressed && (millis() - buttonPressStartTime) >= BUTTON_LEADUP_MS) {
|
||||
|
||||
// Start the progressive sequence if not already active
|
||||
if (!leadUpSequenceActive) {
|
||||
@@ -165,8 +160,6 @@ int32_t ButtonThread::runOnce()
|
||||
|
||||
// Reset when button is released
|
||||
if (!buttonCurrentlyPressed && buttonWasPressed) {
|
||||
if (_releaseHandler)
|
||||
_releaseHandler();
|
||||
leadUpSequenceActive = false;
|
||||
resetLeadUpSequence();
|
||||
}
|
||||
@@ -248,21 +241,7 @@ int32_t ButtonThread::runOnce()
|
||||
this->notifyObservers(&evt);
|
||||
playComboTune();
|
||||
break;
|
||||
#if !HAS_SCREEN
|
||||
case 4:
|
||||
if (moduleConfig.external_notification.enabled && externalNotificationModule) {
|
||||
externalNotificationModule->setMute(!externalNotificationModule->getMute());
|
||||
IF_SCREEN(if (!externalNotificationModule->getMute()) externalNotificationModule->stopNow();)
|
||||
if (externalNotificationModule->getMute()) {
|
||||
LOG_INFO("Temporarily Muted");
|
||||
play4ClickDown(); // Disable tone
|
||||
} else {
|
||||
LOG_INFO("Unmuted");
|
||||
play4ClickUp(); // Enable tone
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
// No valid multipress action
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -13,9 +13,6 @@ struct ButtonConfig {
|
||||
bool activePullup = true;
|
||||
uint32_t pullupSense = 0;
|
||||
voidFuncPtr intRoutine = nullptr;
|
||||
voidFuncPtr onPress = nullptr; // Optional edge callbacks
|
||||
voidFuncPtr onRelease = nullptr; // Optional edge callbacks
|
||||
bool suppressLeadUpSound = false;
|
||||
input_broker_event singlePress = INPUT_BROKER_NONE;
|
||||
input_broker_event longPress = INPUT_BROKER_NONE;
|
||||
uint16_t longPressTime = 500;
|
||||
@@ -97,9 +94,6 @@ class ButtonThread : public Observable<const InputEvent *>, public concurrency::
|
||||
input_broker_event _shortLong = INPUT_BROKER_NONE;
|
||||
|
||||
voidFuncPtr _intRoutine = nullptr;
|
||||
voidFuncPtr _pressHandler = nullptr;
|
||||
voidFuncPtr _releaseHandler = nullptr;
|
||||
bool _suppressLeadUp = false;
|
||||
uint16_t _longPressTime = 500;
|
||||
uint16_t _longLongPressTime = 3900;
|
||||
int _pinNum = 0;
|
||||
|
||||
@@ -39,7 +39,6 @@ enum input_broker_event {
|
||||
#define INPUT_BROKER_MSG_FN_SYMBOL_ON 0xf1
|
||||
#define INPUT_BROKER_MSG_FN_SYMBOL_OFF 0xf2
|
||||
#define INPUT_BROKER_MSG_BLUETOOTH_TOGGLE 0xAA
|
||||
#define INPUT_BROKER_MSG_VOICEMEMO 0xAD
|
||||
#define INPUT_BROKER_MSG_TAB 0x09
|
||||
#define INPUT_BROKER_MSG_EMOTE_LIST 0x8F
|
||||
|
||||
|
||||
@@ -27,9 +27,7 @@ bool RotaryEncoderInterruptImpl1::init()
|
||||
RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB,
|
||||
RotaryEncoderInterruptImpl1::handleIntPressed);
|
||||
inputBroker->registerSource(this);
|
||||
#ifndef HAS_PHYSICAL_KEYBOARD
|
||||
osk_found = true;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,18 +28,18 @@ static unsigned char TCA8418TapMap[_TCA8418_NUM_KEYS][13] = {
|
||||
};
|
||||
|
||||
static unsigned char TCA8418LongPressMap[_TCA8418_NUM_KEYS] = {
|
||||
Key::ESC, // 1
|
||||
Key::UP, // 2
|
||||
Key::NONE, // 3
|
||||
Key::LEFT, // 4
|
||||
Key::NONE, // 5
|
||||
Key::RIGHT, // 6
|
||||
Key::NONE, // 7
|
||||
Key::DOWN, // 8
|
||||
Key::NONE, // 9
|
||||
Key::BSP, // *
|
||||
Key::VOICEMEMO, // 0
|
||||
Key::NONE, // #
|
||||
Key::ESC, // 1
|
||||
Key::UP, // 2
|
||||
Key::NONE, // 3
|
||||
Key::LEFT, // 4
|
||||
Key::NONE, // 5
|
||||
Key::RIGHT, // 6
|
||||
Key::NONE, // 7
|
||||
Key::DOWN, // 8
|
||||
Key::NONE, // 9
|
||||
Key::BSP, // *
|
||||
Key::NONE, // 0
|
||||
Key::NONE, // #
|
||||
};
|
||||
|
||||
TCA8418Keyboard::TCA8418Keyboard()
|
||||
|
||||
@@ -25,7 +25,6 @@ class TCA8418KeyboardBase
|
||||
BT_TOGGLE = 0xAA,
|
||||
GPS_TOGGLE = 0x9E,
|
||||
MUTE_TOGGLE = 0xAC,
|
||||
VOICEMEMO = 0xAD,
|
||||
SEND_PING = 0xAF,
|
||||
BL_TOGGLE = 0xAB
|
||||
};
|
||||
|
||||
@@ -56,8 +56,8 @@ static unsigned char TDeckProTapMap[_TCA8418_NUM_KEYS][5] = {
|
||||
{0x00, 0x00, 0x00}, // Ent, $, m, n, b, v, c, x, z, alt
|
||||
{0x00, 0x00, 0x00},
|
||||
{0x00, 0x00, 0x00},
|
||||
{0x20, 0x00, Key::VOICEMEMO},
|
||||
{Key::VOICEMEMO, 0x00, 0x00},
|
||||
{0x20, 0x00, 0x00},
|
||||
{0x00, 0x00, '0'},
|
||||
{0x00, 0x00, 0x00} // R_Shift, sym, space, mic, L_Shift
|
||||
};
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ static unsigned char TLoraPagerTapMap[_TCA8418_NUM_KEYS][3] = {{'q', 'Q', '1'},
|
||||
{'z', 'Z', '_'},
|
||||
{'x', 'X', '$'},
|
||||
{'c', 'C', ';'},
|
||||
{'v', 'V', Key::VOICEMEMO},
|
||||
{'v', 'V', '?'},
|
||||
{'b', 'B', '!'},
|
||||
{'n', 'N', ','},
|
||||
{'m', 'M', '.'},
|
||||
|
||||
@@ -45,9 +45,7 @@ void TrackballInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLef
|
||||
|
||||
LOG_DEBUG("Trackball GPIO initialized - UP:%d DOWN:%d LEFT:%d RIGHT:%d PRESS:%d", this->_pinUp, this->_pinDown,
|
||||
this->_pinLeft, this->_pinRight, pinPress);
|
||||
#ifndef HAS_PHYSICAL_KEYBOARD
|
||||
osk_found = true;
|
||||
#endif
|
||||
this->setInterval(100);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,7 @@ bool UpDownInterruptImpl1::init()
|
||||
eventDownLong, UpDownInterruptImpl1::handleIntDown, UpDownInterruptImpl1::handleIntUp,
|
||||
UpDownInterruptImpl1::handleIntPressed);
|
||||
inputBroker->registerSource(this);
|
||||
#ifndef HAS_PHYSICAL_KEYBOARD
|
||||
osk_found = true;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+1
-28
@@ -163,16 +163,6 @@ int32_t KbI2cBase::runOnce()
|
||||
e.kbchar = key.key;
|
||||
}
|
||||
break;
|
||||
case 'v': // sym v - voice memo
|
||||
if (is_sym) {
|
||||
e.inputEvent = INPUT_BROKER_ANYKEY;
|
||||
e.kbchar = INPUT_BROKER_MSG_VOICEMEMO;
|
||||
is_sym = false; // reset sym state after second keypress
|
||||
} else {
|
||||
e.inputEvent = INPUT_BROKER_ANYKEY;
|
||||
e.kbchar = key.key;
|
||||
}
|
||||
break;
|
||||
case 0x13: // Code scanner says the SYM key is 0x13
|
||||
is_sym = !is_sym;
|
||||
e.inputEvent = INPUT_BROKER_ANYKEY;
|
||||
@@ -380,10 +370,6 @@ int32_t KbI2cBase::runOnce()
|
||||
|
||||
if (i2cBus->available()) {
|
||||
char c = i2cBus->read();
|
||||
// Debug: log every key press
|
||||
if (c != 0x00) {
|
||||
LOG_DEBUG("T-Deck KB: key=0x%02X ('%c'), is_sym=%d", (uint8_t)c, (c >= 0x20 && c < 0x7f) ? c : '?', is_sym);
|
||||
}
|
||||
InputEvent e = {};
|
||||
e.inputEvent = INPUT_BROKER_NONE;
|
||||
e.source = this->_originName;
|
||||
@@ -457,17 +443,6 @@ int32_t KbI2cBase::runOnce()
|
||||
e.kbchar = c;
|
||||
}
|
||||
break;
|
||||
case 0x76: // letter v. voice memo trigger
|
||||
if (is_sym) {
|
||||
is_sym = false;
|
||||
e.inputEvent = INPUT_BROKER_ANYKEY;
|
||||
e.kbchar = INPUT_BROKER_MSG_VOICEMEMO;
|
||||
LOG_DEBUG("T-Deck: Sym+V pressed, sending VOICEMEMO 0x%02X", INPUT_BROKER_MSG_VOICEMEMO);
|
||||
} else {
|
||||
e.inputEvent = INPUT_BROKER_ANYKEY;
|
||||
e.kbchar = c;
|
||||
}
|
||||
break;
|
||||
case 0x1b: // ESC
|
||||
e.inputEvent = INPUT_BROKER_CANCEL;
|
||||
break;
|
||||
@@ -491,11 +466,9 @@ int32_t KbI2cBase::runOnce()
|
||||
e.inputEvent = INPUT_BROKER_RIGHT;
|
||||
e.kbchar = 0;
|
||||
break;
|
||||
case 0x3F: // Sym key on some T-Deck variants (sends '?')
|
||||
case 0xc: // Modifier key: 0xc is alt+c (Other options could be: 0xea = shift+mic button or 0x4 shift+$(speaker))
|
||||
case 0xc: // Modifier key: 0xc is alt+c (Other options could be: 0xea = shift+mic button or 0x4 shift+$(speaker))
|
||||
// toggle moddifiers button.
|
||||
is_sym = !is_sym;
|
||||
LOG_DEBUG("T-Deck: Modifier key pressed, is_sym now=%d", is_sym);
|
||||
e.inputEvent = INPUT_BROKER_ANYKEY;
|
||||
e.kbchar = is_sym ? INPUT_BROKER_MSG_FN_SYMBOL_ON // send 0xf1 to tell CannedMessages to display that the
|
||||
: INPUT_BROKER_MSG_FN_SYMBOL_OFF; // modifier key is active
|
||||
|
||||
+9
-32
@@ -107,10 +107,6 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
|
||||
|
||||
#if defined(BUTTON_PIN_TOUCH)
|
||||
ButtonThread *TouchButtonThread = nullptr;
|
||||
#if defined(TTGO_T_ECHO_PLUS) && defined(PIN_EINK_EN)
|
||||
static bool touchBacklightWasOn = false;
|
||||
static bool touchBacklightActive = false;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO)
|
||||
@@ -209,7 +205,7 @@ ScanI2C::FoundDevice rgb_found = ScanI2C::FoundDevice(ScanI2C::DeviceType::NONE,
|
||||
/// The I2C address of our Air Quality Indicator (if found)
|
||||
ScanI2C::DeviceAddress aqi_found = ScanI2C::ADDRESS_NONE;
|
||||
|
||||
#ifdef HAS_DRV2605
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
Adafruit_DRV2605 drv;
|
||||
#endif
|
||||
|
||||
@@ -799,6 +795,7 @@ void setup()
|
||||
// We do this as early as possible because this loads preferences from flash
|
||||
// but we need to do this after main cpu init (esp32setup), because we need the random seed set
|
||||
nodeDB = new NodeDB;
|
||||
|
||||
#if HAS_TFT
|
||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||
tftSetup();
|
||||
@@ -844,12 +841,7 @@ void setup()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAS_DRV2605
|
||||
#if defined(PIN_DRV_EN)
|
||||
pinMode(PIN_DRV_EN, OUTPUT);
|
||||
digitalWrite(PIN_DRV_EN, HIGH);
|
||||
delay(10);
|
||||
#endif
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
drv.begin();
|
||||
drv.selectLibrary(1);
|
||||
// I2C trigger by sending 'go' command
|
||||
@@ -885,7 +877,7 @@ void setup()
|
||||
SPI.begin();
|
||||
#endif
|
||||
#else
|
||||
// ESP32
|
||||
// ESP32
|
||||
#if defined(HW_SPI1_DEVICE)
|
||||
SPI1.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
|
||||
LOG_DEBUG("SPI1.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)", LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
|
||||
@@ -1054,24 +1046,6 @@ void setup()
|
||||
};
|
||||
touchConfig.singlePress = INPUT_BROKER_NONE;
|
||||
touchConfig.longPress = INPUT_BROKER_BACK;
|
||||
#if defined(TTGO_T_ECHO_PLUS) && defined(PIN_EINK_EN)
|
||||
// On T-Echo Plus the touch pad should only drive the backlight, not UI navigation/sounds
|
||||
touchConfig.longPress = INPUT_BROKER_NONE;
|
||||
touchConfig.suppressLeadUpSound = true;
|
||||
touchConfig.onPress = []() {
|
||||
touchBacklightWasOn = uiconfig.screen_brightness == 1;
|
||||
if (!touchBacklightWasOn) {
|
||||
digitalWrite(PIN_EINK_EN, HIGH);
|
||||
}
|
||||
touchBacklightActive = true;
|
||||
};
|
||||
touchConfig.onRelease = []() {
|
||||
if (touchBacklightActive && !touchBacklightWasOn) {
|
||||
digitalWrite(PIN_EINK_EN, LOW);
|
||||
}
|
||||
touchBacklightActive = false;
|
||||
};
|
||||
#endif
|
||||
TouchButtonThread->initButton(touchConfig);
|
||||
#endif
|
||||
|
||||
@@ -1212,6 +1186,11 @@ void setup()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef PIN_PWR_DELAY_MS
|
||||
// This may be required to give the peripherals time to power up.
|
||||
delay(PIN_PWR_DELAY_MS);
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_PORTDUINO
|
||||
// as one can't use a function pointer to the class constructor:
|
||||
auto loraModuleInterface = [](LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
|
||||
@@ -1486,10 +1465,8 @@ void setup()
|
||||
#endif
|
||||
|
||||
#if defined(HAS_TRACKBALL) || (defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2)
|
||||
#ifndef HAS_PHYSICAL_KEYBOARD
|
||||
osk_found = true;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER
|
||||
// Start web server thread.
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ extern bool eink_found;
|
||||
extern bool pmu_found;
|
||||
extern bool isUSBPowered;
|
||||
|
||||
#ifdef HAS_DRV2605
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
#include <Adafruit_DRV2605.h>
|
||||
extern Adafruit_DRV2605 drv;
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "configuration.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "meshUtils.h"
|
||||
#include "modules/TextMessageModule.h"
|
||||
#if !MESHTASTIC_EXCLUDE_TRACEROUTE
|
||||
#include "modules/TraceRouteModule.h"
|
||||
#endif
|
||||
@@ -36,10 +35,6 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
return true; // we handled it, so stop processing
|
||||
}
|
||||
|
||||
if (!seenRecently && !wasUpgraded && textMessageModule) {
|
||||
seenRecently = textMessageModule->recentlySeen(p->id);
|
||||
}
|
||||
|
||||
if (seenRecently) {
|
||||
printPacket("Ignore dupe incoming msg", p);
|
||||
rxDupe++;
|
||||
|
||||
@@ -195,7 +195,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
|
||||
// but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs
|
||||
// bad.
|
||||
routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel,
|
||||
routingModule->getHopLimitForResponse(mp));
|
||||
routingModule->getHopLimitForResponse(mp.hop_start, mp.hop_limit));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to)
|
||||
assert(p->which_payload_variant == meshtastic_MeshPacket_decoded_tag); // Should already be set by now
|
||||
p->to = getFrom(&to); // Make sure that if we are sending to the local node, we use our local node addr, not 0
|
||||
p->channel = to.channel; // Use the same channel that the request came in on
|
||||
p->hop_limit = routingModule->getHopLimitForResponse(to);
|
||||
p->hop_limit = routingModule->getHopLimitForResponse(to.hop_start, to.hop_limit);
|
||||
|
||||
// No need for an ack if we are just delivering locally (it just generates an ignored ack)
|
||||
p->want_ack = (to.from != 0) ? to.want_ack : false;
|
||||
|
||||
@@ -95,8 +95,11 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp)
|
||||
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user &&
|
||||
nodeInfoModule && !isPreferredRebroadcaster && !nodeDB->isFull()) {
|
||||
if (airTime->isTxAllowedChannelUtil(true)) {
|
||||
const int8_t hopsUsed = getHopsAway(*mp, config.lora.hop_limit);
|
||||
if (hopsUsed > (int32_t)(config.lora.hop_limit + 2)) {
|
||||
// Hops used by the request. If somebody in between running modified firmware modified it, ignore it
|
||||
auto hopStart = mp->hop_start;
|
||||
auto hopLimit = mp->hop_limit;
|
||||
uint8_t hopsUsed = hopStart < hopLimit ? config.lora.hop_limit : hopStart - hopLimit;
|
||||
if (hopsUsed > config.lora.hop_limit + 2) {
|
||||
LOG_DEBUG("Skip send NodeInfo: %d hops away is too far away", hopsUsed);
|
||||
} else {
|
||||
LOG_INFO("Heard new node on ch. %d, send NodeInfo and ask for response", mp->channel);
|
||||
@@ -192,13 +195,15 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p)
|
||||
|
||||
p.rx_time = getValidTime(RTCQualityFromNet); // Record the time the packet arrived from the phone
|
||||
|
||||
IF_SCREEN(if (p.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP && p.decoded.payload.size > 0 &&
|
||||
p.to != NODENUM_BROADCAST && p.to != 0) // DM only
|
||||
{
|
||||
perhapsDecode(&p);
|
||||
const StoredMessage &sm = messageStore.addFromPacket(p);
|
||||
graphics::MessageRenderer::handleNewMessage(nullptr, sm, p); // notify UI
|
||||
})
|
||||
#if HAS_SCREEN
|
||||
if (screen && p.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP && p.decoded.payload.size > 0 &&
|
||||
p.to != NODENUM_BROADCAST && p.to != 0) // DM only
|
||||
{
|
||||
perhapsDecode(&p);
|
||||
const StoredMessage &sm = messageStore.addFromPacket(p);
|
||||
graphics::MessageRenderer::handleNewMessage(nullptr, sm, p); // notify UI
|
||||
}
|
||||
#endif
|
||||
// Send the packet into the mesh
|
||||
DEBUG_HEAP_BEFORE;
|
||||
auto a = packetPool.allocCopy(p);
|
||||
|
||||
@@ -64,7 +64,7 @@ bool NextHopRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
perhapsRebroadcast(p);
|
||||
}
|
||||
} else {
|
||||
bool isRepeated = getHopsAway(*p) == 0;
|
||||
bool isRepeated = p->hop_start > 0 && p->hop_start == p->hop_limit;
|
||||
// If repeated and not in Tx queue anymore, try relaying again, or if we are the destination, send the ACK again
|
||||
if (isRepeated) {
|
||||
if (!findInTxQueue(p->from, p->id)) {
|
||||
@@ -101,7 +101,8 @@ void NextHopRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtast
|
||||
bool wasAlreadyRelayer = wasRelayer(p->relay_node, p->decoded.request_id, p->to);
|
||||
bool weWereSoleRelayer = false;
|
||||
bool weWereRelayer = wasRelayer(ourRelayID, p->decoded.request_id, p->to, &weWereSoleRelayer);
|
||||
if ((weWereRelayer && wasAlreadyRelayer) || (getHopsAway(*p) == 0 && weWereSoleRelayer)) {
|
||||
if ((weWereRelayer && wasAlreadyRelayer) ||
|
||||
(p->hop_start != 0 && p->hop_start == p->hop_limit && weWereSoleRelayer)) {
|
||||
if (origTx->next_hop != p->relay_node) { // Not already set
|
||||
LOG_INFO("Update next hop of 0x%x to 0x%x based on ACK/reply (was relayer %d we were sole %d)", p->from,
|
||||
p->relay_node, wasAlreadyRelayer, weWereSoleRelayer);
|
||||
|
||||
+3
-22
@@ -805,8 +805,7 @@ void NodeDB::installDefaultModuleConfig()
|
||||
moduleConfig.external_notification.output_ms = 500;
|
||||
moduleConfig.external_notification.nag_timeout = 2;
|
||||
#endif
|
||||
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3) || \
|
||||
defined(ELECROW_ThinkNode_M6)
|
||||
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3)
|
||||
// Default to PIN_LED2 for external notification output (LED color depends on device variant)
|
||||
moduleConfig.external_notification.enabled = true;
|
||||
moduleConfig.external_notification.output = PIN_LED2;
|
||||
@@ -1549,23 +1548,6 @@ uint32_t sinceReceived(const meshtastic_MeshPacket *p)
|
||||
return delta;
|
||||
}
|
||||
|
||||
int8_t getHopsAway(const meshtastic_MeshPacket &p, int8_t defaultIfUnknown)
|
||||
{
|
||||
// Firmware prior to 2.3.0 (585805c) lacked a hop_start field. Firmware version 2.5.0 (bf34329) introduced a
|
||||
// bitfield that is always present. Use the presence of the bitfield to determine if the origin's firmware
|
||||
// version is guaranteed to have hop_start populated. Note that this can only be done for decoded packets as
|
||||
// the bitfield is encrypted under the channel encryption key. For encrypted packets, this returns
|
||||
// defaultIfUnknown when hop_start is 0.
|
||||
if (p.hop_start == 0 && !(p.which_payload_variant == meshtastic_MeshPacket_decoded_tag && p.decoded.has_bitfield))
|
||||
return defaultIfUnknown; // Cannot reliably determine the number of hops.
|
||||
|
||||
// Guard against invalid values.
|
||||
if (p.hop_start < p.hop_limit)
|
||||
return defaultIfUnknown;
|
||||
|
||||
return p.hop_start - p.hop_limit;
|
||||
}
|
||||
|
||||
#define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline
|
||||
|
||||
size_t NodeDB::getNumOnlineMeshNodes(bool localOnly)
|
||||
@@ -1818,10 +1800,9 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
|
||||
info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT
|
||||
|
||||
// If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway
|
||||
const int8_t hopsAway = getHopsAway(mp);
|
||||
if (hopsAway >= 0) {
|
||||
if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) {
|
||||
info->has_hops_away = true;
|
||||
info->hops_away = hopsAway;
|
||||
info->hops_away = mp.hop_start - mp.hop_limit;
|
||||
}
|
||||
sortMeshDB();
|
||||
}
|
||||
|
||||
@@ -110,10 +110,6 @@ uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n);
|
||||
/// Given a packet, return how many seconds in the past (vs now) it was received
|
||||
uint32_t sinceReceived(const meshtastic_MeshPacket *p);
|
||||
|
||||
/// Given a packet, return the number of hops used to reach this node.
|
||||
/// Returns defaultIfUnknown if the number of hops couldn't be determined.
|
||||
int8_t getHopsAway(const meshtastic_MeshPacket &p, int8_t defaultIfUnknown = -1);
|
||||
|
||||
enum LoadFileResult {
|
||||
// Successfully opened the file
|
||||
LOAD_SUCCESS = 1,
|
||||
|
||||
@@ -171,8 +171,7 @@ const RegionInfo regions[] = {
|
||||
863 - 868 MHz <25 mW EIRP, 500kHz channels allowed, must not be used at airfields
|
||||
https://github.com/meshtastic/firmware/issues/7204
|
||||
*/
|
||||
RDEF(KZ_433, 433.075f, 434.775f, 100, 0, 10, true, false, false),
|
||||
RDEF(KZ_863, 863.0f, 868.0f, 100, 0, 30, true, false, false),
|
||||
RDEF(KZ_433, 433.075f, 434.775f, 100, 0, 10, true, false, false), RDEF(KZ_863, 863.0f, 868.0f, 100, 0, 30, true, false, true),
|
||||
|
||||
/*
|
||||
Nepal
|
||||
@@ -246,9 +245,7 @@ uint32_t RadioInterface::getPacketTime(const meshtastic_MeshPacket *p, bool rece
|
||||
/** The delay to use for retransmitting dropped packets */
|
||||
uint32_t RadioInterface::getRetransmissionMsec(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
size_t numbytes = p->which_payload_variant == meshtastic_MeshPacket_decoded_tag
|
||||
? pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded)
|
||||
: p->encrypted.size + MESHTASTIC_HEADER_LENGTH;
|
||||
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded);
|
||||
uint32_t packetAirtime = getPacketTime(numbytes + sizeof(PacketHeader));
|
||||
// Make sure enough time has elapsed for this packet to be sent and an ACK is received.
|
||||
// LOG_DEBUG("Waiting for flooding message with airtime %d and slotTime is %d", packetAirtime, slotTimeMsec);
|
||||
@@ -326,9 +323,9 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(meshtastic_MeshPacket *p)
|
||||
void printPacket(const char *prefix, const meshtastic_MeshPacket *p)
|
||||
{
|
||||
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
|
||||
std::string out =
|
||||
DEBUG_PORT.mt_sprintf("%s (id=0x%08x fr=0x%08x to=0x%08x, transport = %u, WantAck=%d, HopLim=%d Ch=0x%x", prefix, p->id,
|
||||
p->from, p->to, p->transport_mechanism, p->want_ack, p->hop_limit, p->channel);
|
||||
std::string out = DEBUG_PORT.mt_sprintf(
|
||||
"%s (id=0x%08x fr=0x%08x to=0x%08x, transport = %u, WantAck=%d, HopLim=%d HopStart=%d Ch=0x%x", prefix, p->id, p->from,
|
||||
p->to, p->transport_mechanism, p->want_ack, p->hop_limit, p->hop_start, p->channel);
|
||||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||
auto &s = p->decoded;
|
||||
|
||||
@@ -522,10 +519,6 @@ void RadioInterface::applyModemConfig()
|
||||
sf = 12;
|
||||
break;
|
||||
}
|
||||
if (loraConfig.coding_rate >= 5 && loraConfig.coding_rate <= 8 && loraConfig.coding_rate != cr) {
|
||||
cr = loraConfig.coding_rate;
|
||||
LOG_INFO("Using custom Coding Rate %u", cr);
|
||||
}
|
||||
} else {
|
||||
sf = loraConfig.spread_factor;
|
||||
cr = loraConfig.coding_rate;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "ReliableRouter.h"
|
||||
#include "Default.h"
|
||||
#include "MeshTypes.h"
|
||||
#include "NodeDB.h"
|
||||
#include "configuration.h"
|
||||
#include "memGet.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
@@ -109,12 +108,12 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
|
||||
// If this packet should always be ACKed reliably with want_ack back to the original sender, make sure we
|
||||
// do that unconditionally.
|
||||
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel,
|
||||
routingModule->getHopLimitForResponse(*p), true);
|
||||
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit), true);
|
||||
} else if (!p->decoded.request_id && !p->decoded.reply_id) {
|
||||
// If it's not an ACK or a reply, send an ACK.
|
||||
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel,
|
||||
routingModule->getHopLimitForResponse(*p));
|
||||
} else if ((getHopsAway(*p) == 0) || p->next_hop != NO_NEXT_HOP_PREFERENCE) {
|
||||
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
|
||||
} else if ((p->hop_start > 0 && p->hop_start == p->hop_limit) || p->next_hop != NO_NEXT_HOP_PREFERENCE) {
|
||||
// If we received the packet directly from the original sender, send a 0-hop ACK since the original sender
|
||||
// won't overhear any implicit ACKs. If we received the packet via NextHopRouter, also send a 0-hop ACK to
|
||||
// stop the immediate relayer's retransmissions.
|
||||
@@ -124,11 +123,11 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
|
||||
(nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) {
|
||||
LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY");
|
||||
sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(),
|
||||
routingModule->getHopLimitForResponse(*p));
|
||||
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
|
||||
} else {
|
||||
// Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded
|
||||
sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(),
|
||||
routingModule->getHopLimitForResponse(*p));
|
||||
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
|
||||
}
|
||||
} else if (p->next_hop == nodeDB->getLastByteOfNodeNum(getNodeNum()) && p->hop_limit > 0) {
|
||||
// No wantAck, but we need to ACK with hop limit of 0 if we were the next hop to stop their retransmissions
|
||||
|
||||
+13
-17
@@ -81,7 +81,8 @@ Router::Router() : concurrency::OSThread("Router"), fromRadioQueue(MAX_RX_FROMRA
|
||||
bool Router::shouldDecrementHopLimit(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
// First hop MUST always decrement to prevent retry issues
|
||||
if (getHopsAway(*p) == 0) {
|
||||
bool isFirstHop = (p->hop_start != 0 && p->hop_start == p->hop_limit);
|
||||
if (isFirstHop) {
|
||||
return true; // Always decrement on first hop
|
||||
}
|
||||
|
||||
@@ -113,7 +114,7 @@ bool Router::shouldDecrementHopLimit(const meshtastic_MeshPacket *p)
|
||||
|
||||
// Check 3: role check (moderate cost - multiple comparisons)
|
||||
if (!IS_ONE_OF(node->user.role, meshtastic_Config_DeviceConfig_Role_ROUTER,
|
||||
meshtastic_Config_DeviceConfig_Role_ROUTER_LATE, meshtastic_Config_DeviceConfig_Role_CLIENT_BASE)) {
|
||||
meshtastic_Config_DeviceConfig_Role_ROUTER_LATE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -729,8 +730,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
||||
meshtastic_PortNum_POSITION_APP, meshtastic_PortNum_NODEINFO_APP, meshtastic_PortNum_ROUTING_APP,
|
||||
meshtastic_PortNum_TELEMETRY_APP, meshtastic_PortNum_ADMIN_APP, meshtastic_PortNum_ALERT_APP,
|
||||
meshtastic_PortNum_KEY_VERIFICATION_APP, meshtastic_PortNum_WAYPOINT_APP,
|
||||
meshtastic_PortNum_STORE_FORWARD_APP, meshtastic_PortNum_TRACEROUTE_APP,
|
||||
meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP)) {
|
||||
meshtastic_PortNum_STORE_FORWARD_APP, meshtastic_PortNum_TRACEROUTE_APP)) {
|
||||
LOG_DEBUG("Ignore packet on non-standard portnum for CORE_PORTNUMS_ONLY");
|
||||
cancelSending(p->from, p->id);
|
||||
skipHandle = true;
|
||||
@@ -745,19 +745,15 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
||||
MeshModule::callModules(*p, src);
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_MQTT
|
||||
if (p_encrypted == nullptr) {
|
||||
LOG_WARN("p_encrypted is null, skipping MQTT publish");
|
||||
} else {
|
||||
// Mark as pki_encrypted if it is not yet decoded and MQTT encryption is also enabled, hash matches and it's a DM not
|
||||
// to us (because we would be able to decrypt it)
|
||||
if (decodedState == DecodeState::DECODE_FAILURE && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 &&
|
||||
!isBroadcast(p->to) && !isToUs(p))
|
||||
p_encrypted->pki_encrypted = true;
|
||||
// After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet
|
||||
if ((decodedState == DecodeState::DECODE_SUCCESS || p_encrypted->pki_encrypted) && moduleConfig.mqtt.enabled &&
|
||||
!isFromUs(p) && mqtt)
|
||||
mqtt->onSend(*p_encrypted, *p, p->channel);
|
||||
}
|
||||
// Mark as pki_encrypted if it is not yet decoded and MQTT encryption is also enabled, hash matches and it's a DM not to
|
||||
// us (because we would be able to decrypt it)
|
||||
if (decodedState == DecodeState::DECODE_FAILURE && moduleConfig.mqtt.encryption_enabled && p->channel == 0x00 &&
|
||||
!isBroadcast(p->to) && !isToUs(p))
|
||||
p_encrypted->pki_encrypted = true;
|
||||
// After potentially altering it, publish received message to MQTT if we're not the original transmitter of the packet
|
||||
if ((decodedState == DecodeState::DECODE_SUCCESS || p_encrypted->pki_encrypted) && moduleConfig.mqtt.enabled &&
|
||||
!isFromUs(p) && mqtt)
|
||||
mqtt->onSend(*p_encrypted, *p, p->channel);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -53,26 +53,13 @@ template <typename T> bool SX126xInterface<T>::init()
|
||||
#endif
|
||||
|
||||
#if defined(USE_GC1109_PA)
|
||||
// GC1109 FEM chip initialization
|
||||
// See variant.h for full pin mapping and control logic documentation
|
||||
|
||||
// VFEM_Ctrl (LORA_PA_POWER): Power enable for GC1109 LDO (always on)
|
||||
pinMode(LORA_PA_POWER, OUTPUT);
|
||||
digitalWrite(LORA_PA_POWER, HIGH);
|
||||
|
||||
// CSD (LORA_PA_EN): Chip enable - must be HIGH to enable GC1109 for both RX and TX
|
||||
pinMode(LORA_PA_EN, OUTPUT);
|
||||
digitalWrite(LORA_PA_EN, HIGH);
|
||||
|
||||
// CPS (LORA_PA_TX_EN): PA mode select - HIGH enables full PA during TX, LOW for RX (don't care)
|
||||
// Note: TX/RX path switching (CTX) is handled by DIO2 via SX126X_DIO2_AS_RF_SWITCH
|
||||
digitalWrite(LORA_PA_EN, LOW);
|
||||
pinMode(LORA_PA_TX_EN, OUTPUT);
|
||||
digitalWrite(LORA_PA_TX_EN, LOW); // Start in RX-ready state
|
||||
#endif
|
||||
|
||||
#ifdef RF95_FAN_EN
|
||||
digitalWrite(RF95_FAN_EN, HIGH);
|
||||
pinMode(RF95_FAN_EN, OUTPUT);
|
||||
digitalWrite(LORA_PA_TX_EN, LOW);
|
||||
#endif
|
||||
|
||||
#if ARCH_PORTDUINO
|
||||
@@ -98,13 +85,6 @@ template <typename T> bool SX126xInterface<T>::init()
|
||||
power = -9;
|
||||
|
||||
int res = lora.begin(getFreq(), bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage, useRegulatorLDO);
|
||||
|
||||
#ifdef SX126X_PA_RAMP_US
|
||||
// Set custom PA ramp time for boards requiring longer stabilization (e.g., T-Beam 1W needs >800us)
|
||||
if (res == RADIOLIB_ERR_NONE) {
|
||||
lora.setPaRampTime(SX126X_PA_RAMP_US);
|
||||
}
|
||||
#endif
|
||||
// \todo Display actual typename of the adapter, not just `SX126x`
|
||||
LOG_INFO("SX126x init result %d", res);
|
||||
if (res == RADIOLIB_ERR_CHIP_NOT_FOUND || res == RADIOLIB_ERR_SPI_CMD_FAILED)
|
||||
@@ -385,13 +365,13 @@ template <typename T> bool SX126xInterface<T>::sleep()
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Control PA mode for GC1109 FEM - CPS pin selects full PA (txon=true) or bypass mode (txon=false) */
|
||||
/** Some boards require GPIO control of tx vs rx paths */
|
||||
template <typename T> void SX126xInterface<T>::setTransmitEnable(bool txon)
|
||||
{
|
||||
#if defined(USE_GC1109_PA)
|
||||
digitalWrite(LORA_PA_POWER, HIGH); // Ensure LDO is on
|
||||
digitalWrite(LORA_PA_EN, HIGH); // CSD=1: Chip enabled
|
||||
digitalWrite(LORA_PA_TX_EN, txon ? 1 : 0); // CPS: 1=full PA, 0=bypass (for RX, CPS is don't care)
|
||||
digitalWrite(LORA_PA_POWER, HIGH);
|
||||
digitalWrite(LORA_PA_EN, HIGH);
|
||||
digitalWrite(LORA_PA_TX_EN, txon ? 1 : 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,6 @@ PB_BIND(meshtastic_AdminMessage, meshtastic_AdminMessage, 2)
|
||||
PB_BIND(meshtastic_AdminMessage_InputEvent, meshtastic_AdminMessage_InputEvent, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_AdminMessage_OTAEvent, meshtastic_AdminMessage_OTAEvent, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_HamParameters, meshtastic_HamParameters, AUTO)
|
||||
|
||||
|
||||
@@ -36,5 +33,3 @@ PB_BIND(meshtastic_KeyVerificationAdmin, meshtastic_KeyVerificationAdmin, AUTO)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,16 +16,6 @@
|
||||
#endif
|
||||
|
||||
/* Enum definitions */
|
||||
/* Firmware update mode for OTA updates */
|
||||
typedef enum _meshtastic_OTAMode {
|
||||
/* Do not reboot into OTA mode */
|
||||
meshtastic_OTAMode_NO_REBOOT_OTA = 0,
|
||||
/* Reboot into OTA mode for BLE firmware update */
|
||||
meshtastic_OTAMode_OTA_BLE = 1,
|
||||
/* Reboot into OTA mode for WiFi firmware update */
|
||||
meshtastic_OTAMode_OTA_WIFI = 2
|
||||
} meshtastic_OTAMode;
|
||||
|
||||
/* TODO: REPLACE */
|
||||
typedef enum _meshtastic_AdminMessage_ConfigType {
|
||||
/* TODO: REPLACE */
|
||||
@@ -113,17 +103,6 @@ typedef struct _meshtastic_AdminMessage_InputEvent {
|
||||
uint16_t touch_y;
|
||||
} meshtastic_AdminMessage_InputEvent;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(32) meshtastic_AdminMessage_OTAEvent_ota_hash_t;
|
||||
/* User is requesting an over the air update.
|
||||
Node will reboot into the OTA loader */
|
||||
typedef struct _meshtastic_AdminMessage_OTAEvent {
|
||||
/* Tell the node to reboot into OTA mode for firmware update via BLE or WiFi (ESP32 only for now) */
|
||||
meshtastic_OTAMode reboot_ota_mode;
|
||||
/* A 32 byte hash of the OTA firmware.
|
||||
Used to verify the integrity of the firmware before applying an update. */
|
||||
meshtastic_AdminMessage_OTAEvent_ota_hash_t ota_hash;
|
||||
} meshtastic_AdminMessage_OTAEvent;
|
||||
|
||||
/* Parameters for setting up Meshtastic for ameteur radio usage */
|
||||
typedef struct _meshtastic_HamParameters {
|
||||
/* Amateur radio call sign, eg. KD2ABC */
|
||||
@@ -279,13 +258,10 @@ 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)
|
||||
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth.
|
||||
Deprecated in favor of reboot_ota_mode in 2.7.17 */
|
||||
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */
|
||||
int32_t reboot_ota_seconds;
|
||||
/* This message is only supported for the simulator Portduino build.
|
||||
If received the simulator will exit successfully. */
|
||||
@@ -299,8 +275,6 @@ typedef struct _meshtastic_AdminMessage {
|
||||
/* Tell the node to reset the nodedb.
|
||||
When true, favorites are preserved through reset. */
|
||||
bool nodedb_reset;
|
||||
/* Tell the node to reset into the OTA Loader */
|
||||
meshtastic_AdminMessage_OTAEvent ota_request;
|
||||
};
|
||||
/* The node generates this key and sends it with any get_x_response packets.
|
||||
The client MUST include the same key with any set_x commands. Key expires after 300 seconds.
|
||||
@@ -314,10 +288,6 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _meshtastic_OTAMode_MIN meshtastic_OTAMode_NO_REBOOT_OTA
|
||||
#define _meshtastic_OTAMode_MAX meshtastic_OTAMode_OTA_WIFI
|
||||
#define _meshtastic_OTAMode_ARRAYSIZE ((meshtastic_OTAMode)(meshtastic_OTAMode_OTA_WIFI+1))
|
||||
|
||||
#define _meshtastic_AdminMessage_ConfigType_MIN meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG
|
||||
#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG
|
||||
#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG+1))
|
||||
@@ -339,11 +309,8 @@ 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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -353,14 +320,12 @@ extern "C" {
|
||||
/* Initializer values for message structs */
|
||||
#define meshtastic_AdminMessage_init_default {0, {0}, {0, {0}}}
|
||||
#define meshtastic_AdminMessage_InputEvent_init_default {0, 0, 0, 0}
|
||||
#define meshtastic_AdminMessage_OTAEvent_init_default {_meshtastic_OTAMode_MIN, {0, {0}}}
|
||||
#define meshtastic_HamParameters_init_default {"", 0, 0, ""}
|
||||
#define meshtastic_NodeRemoteHardwarePinsResponse_init_default {0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}}
|
||||
#define meshtastic_SharedContact_init_default {0, false, meshtastic_User_init_default, 0, 0}
|
||||
#define meshtastic_KeyVerificationAdmin_init_default {_meshtastic_KeyVerificationAdmin_MessageType_MIN, 0, 0, false, 0}
|
||||
#define meshtastic_AdminMessage_init_zero {0, {0}, {0, {0}}}
|
||||
#define meshtastic_AdminMessage_InputEvent_init_zero {0, 0, 0, 0}
|
||||
#define meshtastic_AdminMessage_OTAEvent_init_zero {_meshtastic_OTAMode_MIN, {0, {0}}}
|
||||
#define meshtastic_HamParameters_init_zero {"", 0, 0, ""}
|
||||
#define meshtastic_NodeRemoteHardwarePinsResponse_init_zero {0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}}
|
||||
#define meshtastic_SharedContact_init_zero {0, false, meshtastic_User_init_zero, 0, 0}
|
||||
@@ -371,8 +336,6 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_InputEvent_kb_char_tag 2
|
||||
#define meshtastic_AdminMessage_InputEvent_touch_x_tag 3
|
||||
#define meshtastic_AdminMessage_InputEvent_touch_y_tag 4
|
||||
#define meshtastic_AdminMessage_OTAEvent_reboot_ota_mode_tag 1
|
||||
#define meshtastic_AdminMessage_OTAEvent_ota_hash_tag 2
|
||||
#define meshtastic_HamParameters_call_sign_tag 1
|
||||
#define meshtastic_HamParameters_tx_power_tag 2
|
||||
#define meshtastic_HamParameters_frequency_tag 3
|
||||
@@ -433,7 +396,6 @@ 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
|
||||
@@ -441,7 +403,6 @@ extern "C" {
|
||||
#define meshtastic_AdminMessage_shutdown_seconds_tag 98
|
||||
#define meshtastic_AdminMessage_factory_reset_config_tag 99
|
||||
#define meshtastic_AdminMessage_nodedb_reset_tag 100
|
||||
#define meshtastic_AdminMessage_ota_request_tag 102
|
||||
#define meshtastic_AdminMessage_session_passkey_tag 101
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
@@ -493,7 +454,6 @@ 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) \
|
||||
@@ -501,8 +461,7 @@ X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_second
|
||||
X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \
|
||||
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payload_variant,nodedb_reset,nodedb_reset), 100) \
|
||||
X(a, STATIC, SINGULAR, BYTES, session_passkey, 101) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ota_request,ota_request), 102)
|
||||
X(a, STATIC, SINGULAR, BYTES, session_passkey, 101)
|
||||
#define meshtastic_AdminMessage_CALLBACK NULL
|
||||
#define meshtastic_AdminMessage_DEFAULT NULL
|
||||
#define meshtastic_AdminMessage_payload_variant_get_channel_response_MSGTYPE meshtastic_Channel
|
||||
@@ -523,7 +482,6 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ota_request,ota_request), 10
|
||||
#define meshtastic_AdminMessage_payload_variant_store_ui_config_MSGTYPE meshtastic_DeviceUIConfig
|
||||
#define meshtastic_AdminMessage_payload_variant_add_contact_MSGTYPE meshtastic_SharedContact
|
||||
#define meshtastic_AdminMessage_payload_variant_key_verification_MSGTYPE meshtastic_KeyVerificationAdmin
|
||||
#define meshtastic_AdminMessage_payload_variant_ota_request_MSGTYPE meshtastic_AdminMessage_OTAEvent
|
||||
|
||||
#define meshtastic_AdminMessage_InputEvent_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, event_code, 1) \
|
||||
@@ -533,12 +491,6 @@ X(a, STATIC, SINGULAR, UINT32, touch_y, 4)
|
||||
#define meshtastic_AdminMessage_InputEvent_CALLBACK NULL
|
||||
#define meshtastic_AdminMessage_InputEvent_DEFAULT NULL
|
||||
|
||||
#define meshtastic_AdminMessage_OTAEvent_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, reboot_ota_mode, 1) \
|
||||
X(a, STATIC, SINGULAR, BYTES, ota_hash, 2)
|
||||
#define meshtastic_AdminMessage_OTAEvent_CALLBACK NULL
|
||||
#define meshtastic_AdminMessage_OTAEvent_DEFAULT NULL
|
||||
|
||||
#define meshtastic_HamParameters_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, STRING, call_sign, 1) \
|
||||
X(a, STATIC, SINGULAR, INT32, tx_power, 2) \
|
||||
@@ -572,7 +524,6 @@ X(a, STATIC, OPTIONAL, UINT32, security_number, 4)
|
||||
|
||||
extern const pb_msgdesc_t meshtastic_AdminMessage_msg;
|
||||
extern const pb_msgdesc_t meshtastic_AdminMessage_InputEvent_msg;
|
||||
extern const pb_msgdesc_t meshtastic_AdminMessage_OTAEvent_msg;
|
||||
extern const pb_msgdesc_t meshtastic_HamParameters_msg;
|
||||
extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePinsResponse_msg;
|
||||
extern const pb_msgdesc_t meshtastic_SharedContact_msg;
|
||||
@@ -581,7 +532,6 @@ extern const pb_msgdesc_t meshtastic_KeyVerificationAdmin_msg;
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define meshtastic_AdminMessage_fields &meshtastic_AdminMessage_msg
|
||||
#define meshtastic_AdminMessage_InputEvent_fields &meshtastic_AdminMessage_InputEvent_msg
|
||||
#define meshtastic_AdminMessage_OTAEvent_fields &meshtastic_AdminMessage_OTAEvent_msg
|
||||
#define meshtastic_HamParameters_fields &meshtastic_HamParameters_msg
|
||||
#define meshtastic_NodeRemoteHardwarePinsResponse_fields &meshtastic_NodeRemoteHardwarePinsResponse_msg
|
||||
#define meshtastic_SharedContact_fields &meshtastic_SharedContact_msg
|
||||
@@ -590,7 +540,6 @@ extern const pb_msgdesc_t meshtastic_KeyVerificationAdmin_msg;
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define MESHTASTIC_MESHTASTIC_ADMIN_PB_H_MAX_SIZE meshtastic_AdminMessage_size
|
||||
#define meshtastic_AdminMessage_InputEvent_size 14
|
||||
#define meshtastic_AdminMessage_OTAEvent_size 36
|
||||
#define meshtastic_AdminMessage_size 511
|
||||
#define meshtastic_HamParameters_size 31
|
||||
#define meshtastic_KeyVerificationAdmin_size 25
|
||||
|
||||
@@ -92,8 +92,8 @@ typedef enum _meshtastic_HardwareModel {
|
||||
Less common/prototype boards listed here (needs one more byte over the air)
|
||||
--------------------------------------------------------------------------- */
|
||||
meshtastic_HardwareModel_LORA_RELAY_V1 = 32,
|
||||
/* T-Echo Plus device from LilyGo */
|
||||
meshtastic_HardwareModel_T_ECHO_PLUS = 33,
|
||||
/* TODO: REPLACE */
|
||||
meshtastic_HardwareModel_NRF52840DK = 33,
|
||||
/* TODO: REPLACE */
|
||||
meshtastic_HardwareModel_PPR = 34,
|
||||
/* TODO: REPLACE */
|
||||
@@ -475,10 +475,7 @@ typedef enum _meshtastic_Routing_Error {
|
||||
meshtastic_Routing_Error_ADMIN_PUBLIC_KEY_UNAUTHORIZED = 37,
|
||||
/* Airtime fairness rate limit exceeded for a packet
|
||||
This typically enforced per portnum and is used to prevent a single node from monopolizing airtime */
|
||||
meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED = 38,
|
||||
/* PKI encryption failed, due to no public key for the remote node
|
||||
This is different from PKI_UNKNOWN_PUBKEY which indicates a failure upon receiving a packet */
|
||||
meshtastic_Routing_Error_PKI_SEND_FAIL_PUBLIC_KEY = 39
|
||||
meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED = 38
|
||||
} meshtastic_Routing_Error;
|
||||
|
||||
/* Enum of message types */
|
||||
@@ -1354,12 +1351,8 @@ extern "C" {
|
||||
#define _meshtastic_Position_AltSource_ARRAYSIZE ((meshtastic_Position_AltSource)(meshtastic_Position_AltSource_ALT_BAROMETRIC+1))
|
||||
|
||||
#define _meshtastic_Routing_Error_MIN meshtastic_Routing_Error_NONE
|
||||
#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_PKI_SEND_FAIL_PUBLIC_KEY
|
||||
#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_PKI_SEND_FAIL_PUBLIC_KEY+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_Routing_Error_MAX meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED
|
||||
#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED+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
|
||||
|
||||
@@ -115,6 +115,7 @@ namespace graphics
|
||||
extern int bannerSignalBars;
|
||||
}
|
||||
extern ScanI2C::DeviceAddress cardkb_found;
|
||||
extern bool graphics::isMuted;
|
||||
extern bool osk_found;
|
||||
|
||||
static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto";
|
||||
|
||||
@@ -89,8 +89,9 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
|
||||
void handleGetCannedMessageModuleMessages(const meshtastic_MeshPacket &req, meshtastic_AdminMessage *response);
|
||||
void handleSetCannedMessageModuleMessages(const char *from_msg);
|
||||
|
||||
// Get current run state (used by VoiceMemoModule to avoid conflicts)
|
||||
#ifdef RAK14014
|
||||
cannedMessageModuleRunState getRunState() const { return runState; }
|
||||
#endif
|
||||
|
||||
// === Packet Interest Filter ===
|
||||
virtual bool wantPacket(const meshtastic_MeshPacket *p) override
|
||||
|
||||
@@ -168,7 +168,7 @@ int32_t ExternalNotificationModule::runOnce()
|
||||
delay = EXT_NOTIFICATION_FAST_THREAD_MS;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_DRV2605
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
drv.go();
|
||||
#endif
|
||||
}
|
||||
@@ -283,7 +283,7 @@ void ExternalNotificationModule::setExternalState(uint8_t index, bool on)
|
||||
#ifdef UNPHONE
|
||||
unphone.rgb(red, green, blue);
|
||||
#endif
|
||||
#ifdef HAS_DRV2605
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
if (on) {
|
||||
drv.go();
|
||||
} else {
|
||||
@@ -319,7 +319,7 @@ void ExternalNotificationModule::stopNow()
|
||||
externalTurnedOn[i] = 0;
|
||||
}
|
||||
setIntervalFromNow(0);
|
||||
#ifdef HAS_DRV2605
|
||||
#if defined(T_WATCH_S3) || defined(T_LORA_PAGER)
|
||||
drv.stop();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
#if ARCH_PORTDUINO
|
||||
#include "input/LinuxInputImpl.h"
|
||||
#include "input/SeesawRotary.h"
|
||||
#include "modules/Native/StoreForwardPlusPlus.h"
|
||||
#include "modules/Telemetry/HostMetrics.h"
|
||||
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
|
||||
#include "modules/StoreForwardModule.h"
|
||||
@@ -87,9 +88,6 @@
|
||||
#if defined(USE_SX1280) && !MESHTASTIC_EXCLUDE_AUDIO
|
||||
#include "modules/esp32/AudioModule.h"
|
||||
#endif
|
||||
#if defined(HAS_I2S) && !MESHTASTIC_EXCLUDE_VOICEMEMO
|
||||
#include "modules/VoiceMemoModule.h"
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
|
||||
#include "modules/esp32/PaxcounterModule.h"
|
||||
#endif
|
||||
@@ -246,6 +244,9 @@ void setupModules()
|
||||
#endif
|
||||
#if ARCH_PORTDUINO
|
||||
new HostMetricsModule();
|
||||
#if SFPP_ENABLED
|
||||
new StoreForwardPlusPlusModule();
|
||||
#endif
|
||||
#endif
|
||||
#if HAS_TELEMETRY
|
||||
new DeviceTelemetryModule();
|
||||
@@ -288,9 +289,6 @@ void setupModules()
|
||||
#if defined(USE_SX1280) && !MESHTASTIC_EXCLUDE_AUDIO
|
||||
audioModule = new AudioModule();
|
||||
#endif
|
||||
#if defined(HAS_I2S) && !MESHTASTIC_EXCLUDE_VOICEMEMO
|
||||
voiceMemoModule = new VoiceMemoModule();
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
|
||||
if (moduleConfig.has_paxcounter && moduleConfig.paxcounter.enabled) {
|
||||
paxcounterModule = new PaxcounterModule();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,224 @@
|
||||
#pragma once
|
||||
#if __has_include("sqlite3.h")
|
||||
#define SFPP_ENABLED 1
|
||||
#include "Channels.h"
|
||||
#include "ProtobufModule.h"
|
||||
#include "Router.h"
|
||||
#include "SinglePortModule.h"
|
||||
#include "sqlite3.h"
|
||||
|
||||
#define SFPP_HASH_SIZE 32
|
||||
#define SFPP_SHORT_HASH_SIZE 8
|
||||
|
||||
/**
|
||||
* Store and forward ++ module
|
||||
* There's an obvious need for a store-and-forward mechanism in Meshtastic.
|
||||
* This module takes heavy inspiration from Git, building a chain of messages that can be synced between nodes.
|
||||
* Each message is hashed, and the chain is built by hashing the previous commit hash and the current message hash.
|
||||
* Nodes can request missing messages by requesting the next message after a given commit hash.
|
||||
*
|
||||
* The current focus is text messages, limited to the primary channel.
|
||||
*
|
||||
* Each chain is identified by a root hash, which is derived from the channelHash, the local nodenum, and the timestamp when
|
||||
* created.
|
||||
*
|
||||
* Each message is also given a message hash, derived from the encrypted payload, the to, from, id.
|
||||
* Notably not the timestamp, as we want these to match across nodes, even if the timestamps differ.
|
||||
*
|
||||
* The authoritative node for the chain will generate a commit hash for each message when adding it to the chain.
|
||||
* The first message's commit hash is derived from the root hash and the message hash.
|
||||
* Subsequent messages' commit hashes are derived from the previous commit hash and the current message hash.
|
||||
* This allows a node to see only the last commit hash, and confirm it hasn't missed any messages.
|
||||
*
|
||||
* Nodes can request the next message in the chain by sending a LINK_REQUEST message with the root hash and the last known commit
|
||||
* hash. Any node that has the next message can respond with a LINK_PROVIDE message containing the next message.
|
||||
*
|
||||
* When a satellite node sees a new text message, it stores it in a scratch database.
|
||||
* These messages are periodically offered to the authoritative node for inclusion in the chain.
|
||||
*
|
||||
* The LINK_PROVIDE message does double-duty, sending both on-chain and off-chain messages.
|
||||
* The differentiator is whether the commit hash is set or left empty.
|
||||
*
|
||||
* When a satellite node receives a canonical link message, it checks if it has the message in scratch.
|
||||
* And evicts it when adding it to the canonical chain.
|
||||
*
|
||||
* This approach allows a node to know whether it has seen a given message before, or if it is new coming via SFPP.
|
||||
* If new, and the timestamp is within the rebroadcast timeout, it will process that message as if it were just received from the
|
||||
* mesh, allowing it to be decrypted, shown to the user, and rebroadcast.
|
||||
*/
|
||||
class StoreForwardPlusPlusModule : public ProtobufModule<meshtastic_StoreForwardPlusPlus>, private concurrency::OSThread
|
||||
{
|
||||
struct link_object {
|
||||
uint32_t to;
|
||||
uint32_t from;
|
||||
uint32_t id;
|
||||
uint32_t rx_time = 0;
|
||||
ChannelHash channel_hash;
|
||||
uint8_t encrypted_bytes[256] = {0};
|
||||
size_t encrypted_len;
|
||||
uint8_t message_hash[SFPP_HASH_SIZE] = {0};
|
||||
size_t message_hash_len = 0;
|
||||
uint8_t root_hash[SFPP_HASH_SIZE] = {0};
|
||||
size_t root_hash_len = 0;
|
||||
uint8_t commit_hash[SFPP_HASH_SIZE] = {0};
|
||||
size_t commit_hash_len = 0;
|
||||
uint32_t counter = 0;
|
||||
std::string payload;
|
||||
bool validObject = true; // set this false when a chain calulation fails, etc.
|
||||
};
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
*
|
||||
*/
|
||||
StoreForwardPlusPlusModule();
|
||||
|
||||
/*
|
||||
-Override the wantPacket method.
|
||||
*/
|
||||
virtual bool wantPacket(const meshtastic_MeshPacket *p) override
|
||||
{
|
||||
switch (p->decoded.portnum) {
|
||||
case meshtastic_PortNum_TEXT_MESSAGE_APP:
|
||||
case meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Called to handle a particular incoming message
|
||||
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
|
||||
it
|
||||
*/
|
||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_StoreForwardPlusPlus *t) override;
|
||||
|
||||
virtual int32_t runOnce() override;
|
||||
|
||||
private:
|
||||
sqlite3 *ppDb;
|
||||
sqlite3_stmt *chain_insert_stmt;
|
||||
sqlite3_stmt *scratch_insert_stmt;
|
||||
sqlite3_stmt *checkDup;
|
||||
sqlite3_stmt *checkScratch;
|
||||
sqlite3_stmt *removeScratch;
|
||||
sqlite3_stmt *updatePayloadStmt;
|
||||
sqlite3_stmt *getPayloadFromScratchStmt;
|
||||
sqlite3_stmt *fromScratchStmt;
|
||||
sqlite3_stmt *fromScratchByHashStmt;
|
||||
sqlite3_stmt *getNextHashStmt;
|
||||
sqlite3_stmt *getChainEndStmt;
|
||||
sqlite3_stmt *getLinkStmt;
|
||||
sqlite3_stmt *getHashFromRootStmt;
|
||||
sqlite3_stmt *addRootToMappingsStmt;
|
||||
sqlite3_stmt *getRootFromChannelHashStmt;
|
||||
sqlite3_stmt *getFullRootHashStmt;
|
||||
sqlite3_stmt *setChainCountStmt;
|
||||
sqlite3_stmt *getChainCountStmt;
|
||||
|
||||
// For a given Meshtastic ChannelHash, fills the root_hash buffer with a 32-byte root hash
|
||||
// returns true if the root hash was found
|
||||
bool getRootFromChannelHash(ChannelHash, uint8_t *);
|
||||
|
||||
// For a given root hash, returns the ChannelHash
|
||||
// can handle partial root hashes
|
||||
ChannelHash getChannelHashFromRoot(uint8_t *_root_hash, size_t);
|
||||
|
||||
// given a root hash and commit hash, returns the next commit hash in the chain
|
||||
// can handle partial root and commit hashes, always fills the buffer with 32 bytes
|
||||
// returns true if a next hash was found
|
||||
bool getNextHash(uint8_t *, size_t, uint8_t *, size_t, uint8_t *);
|
||||
|
||||
// For a given Meshtastic ChannelHash, fills the root_hash buffer with a 32-byte root hash
|
||||
// but this function will add the root hash if it is not already present
|
||||
// returns hash size or 0 if not found/added
|
||||
size_t getOrAddRootFromChannelHash(ChannelHash, uint8_t *);
|
||||
|
||||
// adds the ChannelHash and root_hash to the mappings table
|
||||
void addRootToMappings(ChannelHash, uint8_t *);
|
||||
|
||||
// requests the next message in the chain from the mesh network
|
||||
// Sends a LINK_REQUEST message
|
||||
void requestNextMessage(uint8_t *, size_t, uint8_t *, size_t);
|
||||
|
||||
// request the message X entries from the end.
|
||||
// used to bootstrap a chain, without downloading all of the history
|
||||
void requestMessageCount(uint8_t *, size_t, uint32_t);
|
||||
|
||||
// sends a LINK_PROVIDE message broadcasting the given link object
|
||||
void broadcastLink(uint8_t *, size_t);
|
||||
|
||||
// sends a LINK_PROVIDE message broadcasting the given link object
|
||||
void broadcastLink(link_object &, bool);
|
||||
|
||||
// sends a LINK_PROVIDE message broadcasting the given link object from scratch message store
|
||||
bool sendFromScratch(uint8_t *);
|
||||
|
||||
// Adds the given link object to the canonical chain database
|
||||
bool addToChain(link_object &);
|
||||
|
||||
// Adds an incoming text message to the scratch database
|
||||
bool addToScratch(link_object &);
|
||||
|
||||
// sends a CANON_ANNOUNCE message, specifying the given root and commit hashes
|
||||
void canonAnnounce(uint8_t *, uint8_t *, uint8_t *, uint32_t);
|
||||
|
||||
// checks if the message hash is present in the canonical chain database
|
||||
bool isInDB(uint8_t *, size_t);
|
||||
|
||||
// checks if the message hash is present in the scratch database
|
||||
bool isInScratch(uint8_t *, size_t);
|
||||
|
||||
// retrieves a link object from the scratch database
|
||||
link_object getFromScratch(uint8_t *, size_t);
|
||||
|
||||
// removes a link object from the scratch database
|
||||
void removeFromScratch(uint8_t *, size_t);
|
||||
|
||||
// fills the payload section with the decrypted data for the given message hash
|
||||
// probably not needed for production, but useful for testing
|
||||
void updatePayload(uint8_t *, size_t, std::string);
|
||||
|
||||
// Takes the decrypted MeshPacket and the encrypted packet copy, and builds a link_object
|
||||
// Generates a message hash, but does not set the commit hash
|
||||
link_object ingestTextPacket(const meshtastic_MeshPacket &, const meshtastic_MeshPacket *);
|
||||
|
||||
// ingests a LINK_PROVIDE message and builds a link_object
|
||||
// confirms the root hash and commit hash
|
||||
link_object ingestLinkMessage(meshtastic_StoreForwardPlusPlus *);
|
||||
|
||||
// retrieves a link object from the canonical chain database given a message hash
|
||||
link_object getLink(uint8_t *, size_t);
|
||||
|
||||
// puts the encrypted payload back into the queue as if it were just received
|
||||
void rebroadcastLinkObject(link_object &);
|
||||
|
||||
// Check if an incoming link object's commit hash matches the calculated commit hash
|
||||
bool checkCommitHash(link_object &lo, uint8_t *commit_hash_bytes, size_t hash_len);
|
||||
|
||||
// given a partial root hash, looks up the full 32-byte root hash
|
||||
// returns true if found
|
||||
bool lookUpFullRootHash(uint8_t *partial_root_hash, size_t partial_root_hash_len, uint8_t *full_root_hash);
|
||||
|
||||
// update the mappings table to set the chain count for the given root hash
|
||||
void setChainCount(uint8_t *, size_t, uint32_t);
|
||||
|
||||
// query the mappings table for the chain count for the given root hash
|
||||
uint32_t getChainCount(uint8_t *, size_t);
|
||||
|
||||
link_object getLinkFromCount(uint32_t, uint8_t *, size_t);
|
||||
|
||||
// Track if we have a scheduled runOnce pending
|
||||
// useful to not accudentally delay a scheduled runOnce
|
||||
bool pendingRun = false;
|
||||
|
||||
// Once we have multiple chain types, we can extend this
|
||||
enum chain_types {
|
||||
channel_chain = 0,
|
||||
};
|
||||
|
||||
uint32_t rebroadcastTimeout = 3600; // Messages older than this (in seconds) will not be rebroadcast
|
||||
};
|
||||
#endif
|
||||
@@ -170,7 +170,7 @@ bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp,
|
||||
} else {
|
||||
LOG_DEBUG(" Ignoring dummy neighbor info packet (single neighbor with nodeId 0, snr 0)");
|
||||
}
|
||||
} else if (getHopsAway(mp) == 0) {
|
||||
} else if (mp.hop_start != 0 && mp.hop_start == mp.hop_limit) {
|
||||
LOG_DEBUG("Get or create neighbor: %u with snr %f", mp.from, mp.rx_snr);
|
||||
// If the hopLimit is the same as hopStart, then it is a neighbor
|
||||
getOrCreateNeighbor(mp.from, mp.from, 0,
|
||||
|
||||
@@ -429,9 +429,7 @@ int32_t PositionModule::runOnce()
|
||||
|
||||
if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) {
|
||||
if (waitingForFreshPosition) {
|
||||
#ifdef GPS_DEBUG
|
||||
LOG_DEBUG("Skip initial position send; no fresh position since boot");
|
||||
#endif
|
||||
} else if (nodeDB->hasValidPosition(node)) {
|
||||
lastGpsSend = now;
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ class PositionModule : public ProtobufModule<meshtastic_Position>, private concu
|
||||
// In event mode we want to prevent excessive position broadcasts
|
||||
// we set the minimum interval to 5m
|
||||
const uint32_t minimumTimeThreshold =
|
||||
max(uint32_t(300000), Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30));
|
||||
max(300000, Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30));
|
||||
#else
|
||||
const uint32_t minimumTimeThreshold =
|
||||
Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30);
|
||||
|
||||
@@ -58,11 +58,12 @@ void RoutingModule::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketI
|
||||
router->sendLocal(p); // we sometimes send directly to the local node
|
||||
}
|
||||
|
||||
uint8_t RoutingModule::getHopLimitForResponse(const meshtastic_MeshPacket &mp)
|
||||
uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit)
|
||||
{
|
||||
const int8_t hopsUsed = getHopsAway(mp);
|
||||
if (hopsUsed >= 0) {
|
||||
if (hopsUsed > (int32_t)(config.lora.hop_limit)) {
|
||||
if (hopStart != 0) {
|
||||
// Hops used by the request. If somebody in between running modified firmware modified it, ignore it
|
||||
uint8_t hopsUsed = hopStart < hopLimit ? config.lora.hop_limit : hopStart - hopLimit;
|
||||
if (hopsUsed > config.lora.hop_limit) {
|
||||
// In event mode, we never want to send packets with more than our default 3 hops.
|
||||
#if !(EVENTMODE) // This falls through to the default.
|
||||
return hopsUsed; // If the request used more hops than the limit, use the same amount of hops
|
||||
|
||||
@@ -20,7 +20,7 @@ class RoutingModule : public ProtobufModule<meshtastic_Routing>
|
||||
uint8_t hopLimit = 0);
|
||||
|
||||
// Given the hopStart and hopLimit upon reception of a request, return the hop limit to use for the response
|
||||
uint8_t getHopLimitForResponse(const meshtastic_MeshPacket &mp);
|
||||
uint8_t getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit);
|
||||
|
||||
protected:
|
||||
friend class Router;
|
||||
|
||||
@@ -63,9 +63,9 @@
|
||||
SerialModule *serialModule;
|
||||
SerialModuleRadio *serialModuleRadio;
|
||||
|
||||
#if defined(TTGO_T_ECHO) || defined(TTGO_T_ECHO_PLUS) || defined(CANARYONE) || defined(MESHLINK) || \
|
||||
defined(ELECROW_ThinkNode_M1) || defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE) || \
|
||||
defined(ELECROW_ThinkNode_M3) || defined(MUZI_BASE)
|
||||
#if defined(TTGO_T_ECHO) || defined(CANARYONE) || defined(MESHLINK) || defined(ELECROW_ThinkNode_M1) || \
|
||||
defined(ELECROW_ThinkNode_M5) || defined(HELTEC_MESH_SOLAR) || defined(T_ECHO_LITE) || defined(ELECROW_ThinkNode_M3) || \
|
||||
defined(MUZI_BASE)
|
||||
SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial")
|
||||
{
|
||||
api_type = TYPE_SERIAL;
|
||||
@@ -204,9 +204,8 @@ int32_t SerialModule::runOnce()
|
||||
Serial.begin(baud);
|
||||
Serial.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT);
|
||||
}
|
||||
#elif !defined(TTGO_T_ECHO) && !defined(TTGO_T_ECHO_PLUS) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && \
|
||||
!defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M5) && \
|
||||
!defined(MUZI_BASE)
|
||||
#elif !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(MESHLINK) && \
|
||||
!defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M5) && !defined(MUZI_BASE)
|
||||
if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
|
||||
#ifdef ARCH_RP2040
|
||||
Serial2.setFIFOSize(RX_BUFFER);
|
||||
@@ -262,7 +261,7 @@ int32_t SerialModule::runOnce()
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(TTGO_T_ECHO) && !defined(TTGO_T_ECHO_PLUS) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(MESHLINK) && \
|
||||
#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(MESHLINK) && \
|
||||
!defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M5) && !defined(MUZI_BASE)
|
||||
else if ((moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85)) {
|
||||
processWXSerial();
|
||||
@@ -537,9 +536,9 @@ ParsedLine parseLine(const char *line)
|
||||
*/
|
||||
void SerialModule::processWXSerial()
|
||||
{
|
||||
#if !defined(TTGO_T_ECHO) && !defined(TTGO_T_ECHO_PLUS) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && \
|
||||
!defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && \
|
||||
!defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M5) && !defined(ARCH_STM32WL) && !defined(MUZI_BASE)
|
||||
#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && \
|
||||
!defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M5) && \
|
||||
!defined(ARCH_STM32WL) && !defined(MUZI_BASE)
|
||||
static unsigned int lastAveraged = 0;
|
||||
static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded.
|
||||
static double dir_sum_sin = 0;
|
||||
|
||||
@@ -20,7 +20,7 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
switch (arg->getStatusType()) {
|
||||
case STATUS_TYPE_POWER: {
|
||||
meshtastic::PowerStatus *powerStatus = (meshtastic::PowerStatus *)arg;
|
||||
if (powerStatus->getHasUSB() || powerStatus->getIsCharging()) {
|
||||
if (powerStatus->getHasUSB()) {
|
||||
power_state = charging;
|
||||
if (powerStatus->getBatteryChargePercent() >= 100) {
|
||||
power_state = charged;
|
||||
|
||||
@@ -45,9 +45,10 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event)
|
||||
// Mute
|
||||
case INPUT_BROKER_MSG_MUTE_TOGGLE:
|
||||
if (moduleConfig.external_notification.enabled && externalNotificationModule) {
|
||||
externalNotificationModule->setMute(!externalNotificationModule->getMute());
|
||||
IF_SCREEN(if (!externalNotificationModule->getMute()) externalNotificationModule->stopNow(); screen->showSimpleBanner(
|
||||
externalNotificationModule->getMute() ? "Notifications\nDisabled" : "Notifications\nEnabled", 3000);)
|
||||
bool isMuted = externalNotificationModule->getMute();
|
||||
externalNotificationModule->setMute(!isMuted);
|
||||
IF_SCREEN(graphics::isMuted = !isMuted; if (!isMuted) externalNotificationModule->stopNow();
|
||||
screen->showSimpleBanner(isMuted ? "Notifications\nEnabled" : "Notifications\nDisabled", 3000);)
|
||||
}
|
||||
return 0;
|
||||
// Bluetooth
|
||||
|
||||
@@ -53,7 +53,7 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c
|
||||
#include "Sensor/LTR390UVSensor.h"
|
||||
#endif
|
||||
|
||||
#if __has_include(MESHTASTIC_BME680_HEADER)
|
||||
#if __has_include(<bsec2.h>)
|
||||
#include "Sensor/BME680Sensor.h"
|
||||
#endif
|
||||
|
||||
@@ -214,7 +214,7 @@ void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
|
||||
#if __has_include(<Adafruit_LTR390.h>)
|
||||
addSensor<LTR390UVSensor>(i2cScanner, ScanI2C::DeviceType::LTR390UV);
|
||||
#endif
|
||||
#if __has_include(MESHTASTIC_BME680_HEADER)
|
||||
#if __has_include(<bsec2.h>)
|
||||
addSensor<BME680Sensor>(i2cScanner, ScanI2C::DeviceType::BME_680);
|
||||
#endif
|
||||
#if __has_include(<Adafruit_BMP280.h>)
|
||||
|
||||
@@ -136,12 +136,12 @@ void HealthTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL), tempStr);
|
||||
if (lastMeasurement.variant.health_metrics.has_heart_bpm) {
|
||||
char heartStr[32];
|
||||
snprintf(heartStr, sizeof(heartStr), "Heart Rate: %u bpm", lastMeasurement.variant.health_metrics.heart_bpm);
|
||||
snprintf(heartStr, sizeof(heartStr), "Heart Rate: %.0f bpm", lastMeasurement.variant.health_metrics.heart_bpm);
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL), heartStr);
|
||||
}
|
||||
if (lastMeasurement.variant.health_metrics.has_spO2) {
|
||||
char spo2Str[32];
|
||||
snprintf(spo2Str, sizeof(spo2Str), "spO2: %u %%", lastMeasurement.variant.health_metrics.spO2);
|
||||
snprintf(spo2Str, sizeof(spo2Str), "spO2: %.0f %%", lastMeasurement.variant.health_metrics.spO2);
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL), spo2Str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(MESHTASTIC_BME680_HEADER)
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<bsec2.h>)
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "BME680Sensor.h"
|
||||
@@ -10,7 +10,6 @@
|
||||
|
||||
BME680Sensor::BME680Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BME680, "BME680") {}
|
||||
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
int32_t BME680Sensor::runOnce()
|
||||
{
|
||||
if (!bme680.run()) {
|
||||
@@ -18,13 +17,10 @@ int32_t BME680Sensor::runOnce()
|
||||
}
|
||||
return 35;
|
||||
}
|
||||
#endif // defined(MESHTASTIC_BME680_BSEC2_SUPPORTED)
|
||||
|
||||
bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
|
||||
{
|
||||
status = 0;
|
||||
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
if (!bme680.begin(dev->address.address, *bus))
|
||||
checkStatus("begin");
|
||||
|
||||
@@ -46,25 +42,12 @@ bool BME680Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
|
||||
if (status == 0)
|
||||
LOG_DEBUG("BME680Sensor::runOnce: bme680.status %d", bme680.status);
|
||||
|
||||
#else
|
||||
bme680 = makeBME680(bus);
|
||||
|
||||
if (!bme680->begin(dev->address.address)) {
|
||||
LOG_ERROR("Init sensor: %s failed at begin()", sensorName);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = 1;
|
||||
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
initI2CSensor();
|
||||
return status;
|
||||
}
|
||||
|
||||
bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||
{
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
if (bme680.getData(BSEC_OUTPUT_RAW_PRESSURE).signal == 0)
|
||||
return false;
|
||||
|
||||
@@ -82,27 +65,9 @@ bool BME680Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||
// Check if we need to save state to filesystem (every STATE_SAVE_PERIOD ms)
|
||||
measurement->variant.environment_metrics.iaq = bme680.getData(BSEC_OUTPUT_IAQ).signal;
|
||||
updateState();
|
||||
#else
|
||||
if (!bme680->performReading()) {
|
||||
LOG_ERROR("BME680Sensor::getMetrics: performReading failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
measurement->variant.environment_metrics.has_temperature = true;
|
||||
measurement->variant.environment_metrics.has_relative_humidity = true;
|
||||
measurement->variant.environment_metrics.has_barometric_pressure = true;
|
||||
measurement->variant.environment_metrics.has_gas_resistance = true;
|
||||
|
||||
measurement->variant.environment_metrics.temperature = bme680->readTemperature();
|
||||
measurement->variant.environment_metrics.relative_humidity = bme680->readHumidity();
|
||||
measurement->variant.environment_metrics.barometric_pressure = bme680->readPressure() / 100.0F;
|
||||
measurement->variant.environment_metrics.gas_resistance = bme680->readGas() / 1000.0;
|
||||
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
void BME680Sensor::loadState()
|
||||
{
|
||||
#ifdef FSCom
|
||||
@@ -179,6 +144,5 @@ void BME680Sensor::checkStatus(const char *functionName)
|
||||
else if (bme680.sensor.status > BME68X_OK)
|
||||
LOG_WARN("%s BME68X code: %d", functionName, bme680.sensor.status);
|
||||
}
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,40 +1,23 @@
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(MESHTASTIC_BME680_HEADER)
|
||||
#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include(<bsec2.h>)
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "TelemetrySensor.h"
|
||||
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
#include <bme68xLibrary.h>
|
||||
#include <bsec2.h>
|
||||
#else
|
||||
#include <Adafruit_BME680.h>
|
||||
#include <memory>
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // That's 6 hours worth of millis()
|
||||
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
const uint8_t bsec_config[] = {
|
||||
#include "config/bme680/bme680_iaq_33v_3s_4d/bsec_iaq.txt"
|
||||
};
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
class BME680Sensor : public TelemetrySensor
|
||||
{
|
||||
private:
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
Bsec2 bme680;
|
||||
#else
|
||||
using BME680Ptr = std::unique_ptr<Adafruit_BME680>;
|
||||
|
||||
static BME680Ptr makeBME680(TwoWire *bus) { return std::make_unique<Adafruit_BME680>(bus); }
|
||||
|
||||
BME680Ptr bme680;
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
protected:
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
const char *bsecConfigFileName = "/prefs/bsec.dat";
|
||||
uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0};
|
||||
uint8_t accuracy = 0;
|
||||
@@ -51,13 +34,10 @@ class BME680Sensor : public TelemetrySensor
|
||||
void loadState();
|
||||
void updateState();
|
||||
void checkStatus(const char *functionName);
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
|
||||
public:
|
||||
BME680Sensor();
|
||||
#if MESHTASTIC_BME680_BSEC2_SUPPORTED == 1
|
||||
virtual int32_t runOnce() override;
|
||||
#endif // MESHTASTIC_BME680_BSEC2_SUPPORTED
|
||||
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||
virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
|
||||
};
|
||||
|
||||
@@ -17,24 +17,22 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp
|
||||
auto &p = mp.decoded;
|
||||
LOG_INFO("Received text msg from=0x%0x, id=0x%x, msg=%.*s", mp.from, mp.id, p.payload.size, p.payload.bytes);
|
||||
#endif
|
||||
// add packet ID to the rolling list of packets
|
||||
textPacketList[textPacketListIndex] = mp.id;
|
||||
textPacketListIndex = (textPacketListIndex + 1) % TEXT_PACKET_LIST_SIZE;
|
||||
|
||||
// We only store/display messages destined for us.
|
||||
devicestate.rx_text_message = mp;
|
||||
devicestate.has_rx_text_message = true;
|
||||
IF_SCREEN(
|
||||
// Guard against running in MeshtasticUI or with no screen
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||
// Store in the central message history
|
||||
const StoredMessage &sm = messageStore.addFromPacket(mp);
|
||||
#if HAS_SCREEN
|
||||
// Guard against running in MeshtasticUI
|
||||
if (screen && config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||
// Store in the central message history
|
||||
const StoredMessage &sm = messageStore.addFromPacket(mp);
|
||||
|
||||
// Pass message to renderer (banner + thread switching + scroll reset)
|
||||
// Use the global Screen singleton to retrieve the current OLED display
|
||||
auto *display = screen ? screen->getDisplayDevice() : nullptr;
|
||||
graphics::MessageRenderer::handleNewMessage(display, sm, mp);
|
||||
})
|
||||
// Pass message to renderer (banner + thread switching + scroll reset)
|
||||
// Use the global Screen singleton to retrieve the current OLED display
|
||||
auto *display = screen ? screen->getDisplayDevice() : nullptr;
|
||||
graphics::MessageRenderer::handleNewMessage(display, sm, mp);
|
||||
}
|
||||
#endif
|
||||
// Only trigger screen wake if configuration allows it
|
||||
if (shouldWakeOnReceivedMessage()) {
|
||||
powerFSM.trigger(EVENT_RECEIVED_MSG);
|
||||
@@ -50,13 +48,3 @@ bool TextMessageModule::wantPacket(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
return MeshService::isTextPayload(p);
|
||||
}
|
||||
|
||||
bool TextMessageModule::recentlySeen(uint32_t id)
|
||||
{
|
||||
for (size_t i = 0; i < TEXT_PACKET_LIST_SIZE; i++) {
|
||||
if (textPacketList[i] != 0 && textPacketList[i] == id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include "Observer.h"
|
||||
#include "SinglePortModule.h"
|
||||
#define TEXT_PACKET_LIST_SIZE 50
|
||||
|
||||
/**
|
||||
* Text message handling for Meshtastic.
|
||||
@@ -20,8 +19,6 @@ class TextMessageModule : public SinglePortModule, public Observable<const mesht
|
||||
*/
|
||||
TextMessageModule() : SinglePortModule("text", meshtastic_PortNum_TEXT_MESSAGE_APP) {}
|
||||
|
||||
bool recentlySeen(uint32_t id);
|
||||
|
||||
protected:
|
||||
/** Called to handle a particular incoming message
|
||||
*
|
||||
@@ -30,10 +27,6 @@ class TextMessageModule : public SinglePortModule, public Observable<const mesht
|
||||
*/
|
||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
||||
virtual bool wantPacket(const meshtastic_MeshPacket *p) override;
|
||||
|
||||
private:
|
||||
uint32_t textPacketList[TEXT_PACKET_LIST_SIZE] = {0};
|
||||
size_t textPacketListIndex = 0;
|
||||
};
|
||||
|
||||
extern TextMessageModule *textMessageModule;
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "TraceRouteModule.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "graphics/ScreenFonts.h"
|
||||
#include "graphics/SharedUIDisplay.h"
|
||||
@@ -360,10 +359,10 @@ void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_Ro
|
||||
}
|
||||
|
||||
// Only insert unknown hops if hop_start is valid
|
||||
const int8_t hopsTaken = getHopsAway(p);
|
||||
if (hopsTaken >= 0) {
|
||||
if (p.hop_start != 0 && p.hop_limit <= p.hop_start) {
|
||||
uint8_t hopsTaken = p.hop_start - p.hop_limit;
|
||||
int8_t diff = hopsTaken - *route_count;
|
||||
for (int8_t i = 0; i < diff; i++) {
|
||||
for (uint8_t i = 0; i < diff; i++) {
|
||||
if (*route_count < ROUTE_SIZE) {
|
||||
route[*route_count] = NODENUM_BROADCAST; // This will represent an unknown hop
|
||||
*route_count += 1;
|
||||
@@ -371,7 +370,7 @@ void TraceRouteModule::insertUnknownHops(meshtastic_MeshPacket &p, meshtastic_Ro
|
||||
}
|
||||
// Add unknown SNR values if necessary
|
||||
diff = *route_count - *snr_count;
|
||||
for (int8_t i = 0; i < diff; i++) {
|
||||
for (uint8_t i = 0; i < diff; i++) {
|
||||
if (*snr_count < ROUTE_SIZE) {
|
||||
snr_list[*snr_count] = INT8_MIN; // This will represent an unknown SNR
|
||||
*snr_count += 1;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,180 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "SinglePortModule.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include "input/InputBroker.h"
|
||||
#include "mesh/generated/meshtastic/module_config.pb.h"
|
||||
|
||||
#if defined(ARCH_ESP32) && defined(HAS_I2S) && !MESHTASTIC_EXCLUDE_VOICEMEMO
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ButterworthFilter.h>
|
||||
#include <OLEDDisplay.h>
|
||||
#include <OLEDDisplayUi.h>
|
||||
#include <codec2.h>
|
||||
#include <driver/i2s.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
|
||||
/**
|
||||
* VoiceMemoModule - Store and forward short codec2 encoded audio messages
|
||||
*
|
||||
* Unlike the existing AudioModule which is designed for real-time push-to-talk,
|
||||
* this module is designed for short voice memos that are:
|
||||
* - Recorded when the user holds Shift+Space
|
||||
* - Encoded with Codec2 for compression
|
||||
* - Sent over the mesh with hop_limit=0 (local only)
|
||||
* - Stored on receiving devices for later playback
|
||||
* - Played back when user long-presses on the notification
|
||||
*/
|
||||
|
||||
// Voice memo states
|
||||
enum class VoiceMemoState { IDLE, RECORDING, SENDING, RECEIVING, PLAYING };
|
||||
|
||||
// Codec2 magic header for voice memos
|
||||
const char VOICEMEMO_MAGIC[4] = {0xc0, 0xde, 0xc2, 0x4d}; // c0dec2M (M for Memo)
|
||||
|
||||
struct VoiceMemoHeader {
|
||||
char magic[4];
|
||||
uint8_t mode; // Codec2 mode
|
||||
uint8_t sequence; // Packet sequence number (for multi-packet memos)
|
||||
uint8_t totalParts; // Total number of packets in this memo (0 = unknown/streaming)
|
||||
uint8_t memoId; // Unique ID for this recording session (to identify related packets)
|
||||
};
|
||||
|
||||
// Maximum recording time in seconds
|
||||
#define VOICEMEMO_MAX_RECORD_SECS 10
|
||||
#define VOICEMEMO_ADC_BUFFER_SIZE 320 // Codec2 samples per frame
|
||||
#define VOICEMEMO_UPSAMPLE_BUFFER_SIZE 3600 // 320 * (44100/8000) * 2 (stereo) ≈ 3528, rounded up
|
||||
#define VOICEMEMO_I2S_PORT I2S_NUM_0
|
||||
// Codec2 mode - use protobuf enum minus 1 to get codec2 library mode
|
||||
#define VOICEMEMO_CODEC2_MODE (meshtastic_ModuleConfig_AudioConfig_Audio_Baud_CODEC2_700 - 1)
|
||||
|
||||
// Storage for received voice memos
|
||||
#define VOICEMEMO_MAX_STORED 5
|
||||
struct StoredVoiceMemo {
|
||||
NodeNum from;
|
||||
uint32_t timestamp;
|
||||
uint8_t data[meshtastic_Constants_DATA_PAYLOAD_LEN * 4]; // Allow up to 4 packets
|
||||
size_t dataLen;
|
||||
uint8_t codec2Mode;
|
||||
uint8_t memoId; // Memo ID from sender (to identify related packets)
|
||||
uint8_t receivedParts; // Bitmask of received packet sequences
|
||||
uint8_t expectedParts; // Total expected parts (0 = unknown)
|
||||
bool played;
|
||||
};
|
||||
|
||||
class VoiceMemoModule : public SinglePortModule, public Observable<const UIFrameEvent *>, private concurrency::OSThread
|
||||
{
|
||||
public:
|
||||
VoiceMemoModule();
|
||||
|
||||
/**
|
||||
* Check if we should draw the UI frame
|
||||
*/
|
||||
bool shouldDraw();
|
||||
|
||||
/**
|
||||
* Handle keyboard input for Shift+Space detection
|
||||
*/
|
||||
int handleInputEvent(const InputEvent *event);
|
||||
|
||||
/**
|
||||
* Play a stored voice memo
|
||||
*/
|
||||
void playStoredMemo(int index);
|
||||
|
||||
/**
|
||||
* Get number of unplayed memos
|
||||
*/
|
||||
int getUnplayedCount();
|
||||
|
||||
/**
|
||||
* Get stored memo info for UI
|
||||
*/
|
||||
const StoredVoiceMemo *getStoredMemo(int index);
|
||||
|
||||
protected:
|
||||
virtual int32_t runOnce() override;
|
||||
virtual meshtastic_MeshPacket *allocReply() override;
|
||||
virtual bool wantUIFrame() override { return shouldDraw(); }
|
||||
virtual Observable<const UIFrameEvent *> *getUIFrameObservable() override { return this; }
|
||||
|
||||
#if HAS_SCREEN
|
||||
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
|
||||
#endif
|
||||
|
||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
||||
|
||||
private:
|
||||
// State machine
|
||||
VoiceMemoState state = VoiceMemoState::IDLE;
|
||||
|
||||
// Codec2
|
||||
CODEC2 *codec2 = nullptr;
|
||||
int encodeCodecSize = 0;
|
||||
int adcBufferSize = 0;
|
||||
|
||||
// Audio buffers
|
||||
int16_t speechBuffer[VOICEMEMO_ADC_BUFFER_SIZE] = {};
|
||||
int16_t outputBuffer[VOICEMEMO_ADC_BUFFER_SIZE] = {};
|
||||
int16_t upsampleBuffer[VOICEMEMO_UPSAMPLE_BUFFER_SIZE] = {}; // For 8kHz->44.1kHz upsampling
|
||||
uint8_t encodedFrame[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
|
||||
size_t encodedFrameIndex = 0;
|
||||
|
||||
// Recording state
|
||||
uint32_t recordingStartMs = 0;
|
||||
uint32_t sendingCompleteMs = 0; // When sending completed (for "Sent!" display timeout)
|
||||
uint8_t currentMemoId = 0; // Unique ID for current recording session
|
||||
uint8_t currentSequence = 0; // Current packet sequence number
|
||||
|
||||
// I2S state
|
||||
bool i2sInitialized = false;
|
||||
|
||||
// Stored memos for playback
|
||||
StoredVoiceMemo storedMemos[VOICEMEMO_MAX_STORED];
|
||||
int storedMemoCount = 0;
|
||||
|
||||
// Playback state
|
||||
int playingMemoIndex = -1;
|
||||
size_t playbackPosition = 0;
|
||||
|
||||
// Filter for audio cleanup
|
||||
ButterworthFilter *hpFilter = nullptr;
|
||||
|
||||
// Codec2 task for encoding (needs large stack)
|
||||
TaskHandle_t codec2TaskHandle = nullptr;
|
||||
volatile bool codec2TaskRunning = false;
|
||||
volatile bool audioReady = false;
|
||||
|
||||
// Playback task (also needs large stack for Codec2 decoding)
|
||||
TaskHandle_t playbackTaskHandle = nullptr;
|
||||
volatile bool playbackTaskRunning = false;
|
||||
volatile bool playbackReady = false;
|
||||
const StoredVoiceMemo *currentPlaybackMemo = nullptr;
|
||||
|
||||
// Internal methods
|
||||
bool initES7210();
|
||||
bool initI2S();
|
||||
void deinitI2S();
|
||||
void startRecording();
|
||||
void stopRecording();
|
||||
void processRecordingBuffer();
|
||||
void sendEncodedPayload();
|
||||
void storeMemo(const meshtastic_MeshPacket &mp);
|
||||
void playMemo(const StoredVoiceMemo &memo);
|
||||
|
||||
public:
|
||||
// Called by the codec2 task - needs to be public for task function access
|
||||
void doCodec2Encode();
|
||||
void doCodec2Playback();
|
||||
|
||||
// Keyboard observer
|
||||
CallbackObserver<VoiceMemoModule, const InputEvent *> inputObserver =
|
||||
CallbackObserver<VoiceMemoModule, const InputEvent *>(this, &VoiceMemoModule::handleInputEvent);
|
||||
};
|
||||
|
||||
extern VoiceMemoModule *voiceMemoModule;
|
||||
|
||||
#endif // ARCH_ESP32 && HAS_I2S && !MESHTASTIC_EXCLUDE_VOICEMEMO
|
||||
+70
-139
@@ -14,11 +14,11 @@
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
#include "NimBLEAdvertising.h"
|
||||
#ifdef CONFIG_BT_NIMBLE_EXT_ADV
|
||||
#include "NimBLEExtAdvertising.h"
|
||||
#include "PowerStatus.h"
|
||||
#endif
|
||||
#include "PowerStatus.h"
|
||||
|
||||
#if defined(CONFIG_NIMBLE_CPP_IDF)
|
||||
#include "host/ble_gap.h"
|
||||
@@ -26,15 +26,12 @@
|
||||
#include "nimble/nimble/host/include/host/ble_gap.h"
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr uint16_t kPreferredBleMtu = 517;
|
||||
constexpr uint16_t kPreferredBleTxOctets = 251;
|
||||
constexpr uint16_t kPreferredBleTxTimeUs = (kPreferredBleTxOctets + 14) * 8;
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
// Debugging options: careful, they slow things down quite a bit!
|
||||
// #define DEBUG_NIMBLE_ON_READ_TIMING // uncomment to time onRead duration
|
||||
@@ -313,11 +310,9 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
{
|
||||
PhoneAPI::onNowHasData(fromRadioNum);
|
||||
|
||||
int currentNotifyCount = notifyCount.fetch_add(1);
|
||||
|
||||
uint8_t cc = bleServer->getConnectedCount();
|
||||
|
||||
#ifdef DEBUG_NIMBLE_NOTIFY
|
||||
int currentNotifyCount = notifyCount.fetch_add(1);
|
||||
uint8_t cc = bleServer->getConnectedCount();
|
||||
// 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
|
||||
@@ -326,13 +321,7 @@ class BluetoothPhoneAPI : public PhoneAPI, public concurrency::OSThread
|
||||
put_le32(val, fromRadioNum);
|
||||
|
||||
fromNumCharacteristic->setValue(val, sizeof(val));
|
||||
#ifdef NIMBLE_TWO
|
||||
// NOTE: I don't have any NIMBLE_TWO devices, but this line makes me suspicious, and I suspect it needs to just be
|
||||
// notify().
|
||||
fromNumCharacteristic->notify(val, sizeof(val), BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
fromNumCharacteristic->notify();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
@@ -397,12 +386,7 @@ static uint8_t lastToRadio[MAX_TO_FROM_RADIO_SIZE];
|
||||
|
||||
class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo)
|
||||
#else
|
||||
virtual void onWrite(NimBLECharacteristic *pCharacteristic)
|
||||
|
||||
#endif
|
||||
void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &) override
|
||||
{
|
||||
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
|
||||
// Assumption: onWrite is serialized by NimBLE, so we don't need to lock here against multiple concurrent onWrite calls.
|
||||
@@ -449,11 +433,7 @@ class NimbleBluetoothToRadioCallback : public NimBLECharacteristicCallbacks
|
||||
|
||||
class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo)
|
||||
#else
|
||||
virtual void onRead(NimBLECharacteristic *pCharacteristic)
|
||||
#endif
|
||||
void onRead(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &) override
|
||||
{
|
||||
// CAUTION: This callback runs in the NimBLE task!!! Don't do anything except communicate with the main task's runOnce.
|
||||
|
||||
@@ -561,32 +541,27 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
||||
|
||||
class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
public:
|
||||
NimbleBluetoothServerCallback(NimbleBluetooth *ble) { this->ble = ble; }
|
||||
explicit NimbleBluetoothServerCallback(NimbleBluetooth *ble) : ble(ble) {}
|
||||
|
||||
private:
|
||||
NimbleBluetooth *ble;
|
||||
|
||||
virtual uint32_t onPassKeyDisplay()
|
||||
#else
|
||||
virtual uint32_t onPassKeyRequest()
|
||||
#endif
|
||||
uint32_t onPassKeyDisplay() override
|
||||
{
|
||||
uint32_t passkey = config.bluetooth.fixed_pin;
|
||||
|
||||
if (config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_RANDOM_PIN) {
|
||||
LOG_INFO("Use random passkey");
|
||||
// This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
|
||||
passkey = random(100000, 999999);
|
||||
}
|
||||
LOG_INFO("*** Enter passkey %d on the peer side ***", passkey);
|
||||
LOG_INFO("*** Enter passkey %06u on the peer side ***", passkey);
|
||||
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
meshtastic::BluetoothStatus newStatus(std::to_string(passkey));
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
|
||||
#if HAS_SCREEN // Todo: migrate this display code back into Screen class, and observe bluetoothStatus
|
||||
#if HAS_SCREEN
|
||||
if (screen) {
|
||||
screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
char btPIN[16] = "888888";
|
||||
@@ -615,39 +590,29 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
});
|
||||
}
|
||||
#endif
|
||||
passkeyShowing = true;
|
||||
|
||||
passkeyShowing = true;
|
||||
return passkey;
|
||||
}
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onAuthenticationComplete(NimBLEConnInfo &connInfo)
|
||||
#else
|
||||
virtual void onAuthenticationComplete(ble_gap_conn_desc *desc)
|
||||
#endif
|
||||
void onAuthenticationComplete(NimBLEConnInfo &connInfo) override
|
||||
{
|
||||
LOG_INFO("BLE authentication complete");
|
||||
|
||||
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
|
||||
// Todo: migrate this display code back into Screen class, and observe bluetoothStatus
|
||||
if (passkeyShowing) {
|
||||
passkeyShowing = false;
|
||||
if (screen)
|
||||
if (screen) {
|
||||
screen->endAlert();
|
||||
}
|
||||
}
|
||||
|
||||
// Store the connection handle for future use
|
||||
#ifdef NIMBLE_TWO
|
||||
nimbleBluetoothConnHandle = connInfo.getConnHandle();
|
||||
#else
|
||||
nimbleBluetoothConnHandle = desc->conn_handle;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo)
|
||||
void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override
|
||||
{
|
||||
LOG_INFO("BLE incoming connection %s", connInfo.getAddress().toString().c_str());
|
||||
|
||||
@@ -672,21 +637,12 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
LOG_INFO("BLE conn %u initial MTU %u (target %u)", connHandle, connInfo.getMTU(), kPreferredBleMtu);
|
||||
pServer->updateConnParams(connHandle, 6, 12, 0, 200);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
virtual void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason)
|
||||
void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override
|
||||
{
|
||||
LOG_INFO("BLE disconnect reason: %d", reason);
|
||||
#else
|
||||
virtual void onDisconnect(NimBLEServer *pServer, ble_gap_conn_desc *desc)
|
||||
{
|
||||
LOG_INFO("BLE disconnect");
|
||||
#endif
|
||||
#ifdef NIMBLE_TWO
|
||||
if (ble->isDeInit)
|
||||
return;
|
||||
#endif
|
||||
|
||||
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::DISCONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
@@ -710,35 +666,69 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
bluetoothPhoneAPI->writeCount = 0;
|
||||
}
|
||||
|
||||
// Clear the last ToRadio packet buffer to avoid rejecting first packet from new connection
|
||||
memset(lastToRadio, 0, sizeof(lastToRadio));
|
||||
|
||||
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE; // BLE_HS_CONN_HANDLE_NONE means "no connection"
|
||||
nimbleBluetoothConnHandle = BLE_HS_CONN_HANDLE_NONE;
|
||||
|
||||
#ifdef NIMBLE_TWO
|
||||
// Restart Advertising
|
||||
ble->startAdvertising();
|
||||
#else
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
if (!pAdvertising->start(0)) {
|
||||
if (pAdvertising->isAdvertising()) {
|
||||
LOG_DEBUG("BLE advertising already running");
|
||||
} else {
|
||||
LOG_ERROR("BLE failed to restart advertising");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static NimbleBluetoothToRadioCallback *toRadioCallbacks;
|
||||
static NimbleBluetoothFromRadioCallback *fromRadioCallbacks;
|
||||
|
||||
void NimbleBluetooth::startAdvertising()
|
||||
{
|
||||
#if defined(CONFIG_BT_NIMBLE_EXT_ADV)
|
||||
NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
NimBLEExtAdvertisement legacyAdvertising;
|
||||
|
||||
legacyAdvertising.setLegacyAdvertising(true);
|
||||
legacyAdvertising.setScannable(true);
|
||||
legacyAdvertising.setConnectable(true);
|
||||
legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN);
|
||||
if (powerStatus->getHasBattery() == 1) {
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f));
|
||||
}
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID));
|
||||
legacyAdvertising.setMinInterval(500);
|
||||
legacyAdvertising.setMaxInterval(1000);
|
||||
|
||||
NimBLEExtAdvertisement legacyScanResponse;
|
||||
legacyScanResponse.setLegacyAdvertising(true);
|
||||
legacyScanResponse.setConnectable(true);
|
||||
legacyScanResponse.setName(getDeviceName());
|
||||
|
||||
if (!pAdvertising->setInstanceData(0, legacyAdvertising)) {
|
||||
LOG_ERROR("BLE failed to set legacyAdvertising");
|
||||
} else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) {
|
||||
LOG_ERROR("BLE failed to set legacyScanResponse");
|
||||
} else if (!pAdvertising->start(0, 0, 0)) {
|
||||
LOG_ERROR("BLE failed to start legacyAdvertising");
|
||||
}
|
||||
#else
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->reset();
|
||||
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
|
||||
if (powerStatus->getHasBattery() == 1) {
|
||||
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f));
|
||||
}
|
||||
|
||||
NimBLEAdvertisementData scan;
|
||||
scan.setName(getDeviceName());
|
||||
pAdvertising->setScanResponseData(scan);
|
||||
pAdvertising->enableScanResponse(true);
|
||||
|
||||
if (!pAdvertising->start(0)) {
|
||||
LOG_ERROR("BLE failed to start advertising");
|
||||
}
|
||||
#endif
|
||||
LOG_DEBUG("BLE Advertising started");
|
||||
}
|
||||
|
||||
void NimbleBluetooth::shutdown()
|
||||
{
|
||||
// No measurable power saving for ESP32 during light-sleep(?)
|
||||
#ifndef ARCH_ESP32
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
LOG_INFO("Disable bluetooth");
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->reset();
|
||||
@@ -746,7 +736,6 @@ void NimbleBluetooth::shutdown()
|
||||
#endif
|
||||
}
|
||||
|
||||
// Proper shutdown for ESP32. Needs reboot to reverse.
|
||||
void NimbleBluetooth::deinit()
|
||||
{
|
||||
#ifdef ARCH_ESP32
|
||||
@@ -760,21 +749,17 @@ void NimbleBluetooth::deinit()
|
||||
digitalWrite(BLE_LED, LOW);
|
||||
#endif
|
||||
#endif
|
||||
#ifndef NIMBLE_TWO
|
||||
NimBLEDevice::deinit();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Has initial setup been completed
|
||||
bool NimbleBluetooth::isActive()
|
||||
{
|
||||
return bleServer;
|
||||
return bleServer != nullptr;
|
||||
}
|
||||
|
||||
bool NimbleBluetooth::isConnected()
|
||||
{
|
||||
return bleServer->getConnectedCount() > 0;
|
||||
return bleServer && bleServer->getConnectedCount() > 0;
|
||||
}
|
||||
|
||||
int NimbleBluetooth::getRssi()
|
||||
@@ -818,7 +803,7 @@ void NimbleBluetooth::setup()
|
||||
LOG_INFO("Init the NimBLE bluetooth module");
|
||||
|
||||
NimBLEDevice::init(getDeviceName());
|
||||
NimBLEDevice::setPower(ESP_PWR_LVL_P9);
|
||||
NimBLEDevice::setPower(9);
|
||||
|
||||
#if NIMBLE_ENABLE_2M_PHY && (defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6))
|
||||
int mtuResult = NimBLEDevice::setMTU(kPreferredBleMtu);
|
||||
@@ -851,11 +836,7 @@ void NimbleBluetooth::setup()
|
||||
NimBLEDevice::setSecurityIOCap(BLE_HS_IO_DISPLAY_ONLY);
|
||||
}
|
||||
bleServer = NimBLEDevice::createServer();
|
||||
#ifdef NIMBLE_TWO
|
||||
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback(this);
|
||||
#else
|
||||
NimbleBluetoothServerCallback *serverCallbacks = new NimbleBluetoothServerCallback();
|
||||
#endif
|
||||
auto *serverCallbacks = new NimbleBluetoothServerCallback(this);
|
||||
bleServer->setCallbacks(serverCallbacks, true);
|
||||
setupService();
|
||||
startAdvertising();
|
||||
@@ -900,11 +881,7 @@ void NimbleBluetooth::setupService()
|
||||
NimBLEService *batteryService = bleServer->createService(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
||||
BatteryCharacteristic = batteryService->createCharacteristic( // 0x2A19 is the Battery Level characteristic)
|
||||
(uint16_t)0x2a19, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY, 1);
|
||||
#ifdef NIMBLE_TWO
|
||||
NimBLE2904 *batteryLevelDescriptor = BatteryCharacteristic->create2904();
|
||||
#else
|
||||
NimBLE2904 *batteryLevelDescriptor = (NimBLE2904 *)BatteryCharacteristic->createDescriptor((uint16_t)0x2904);
|
||||
#endif
|
||||
batteryLevelDescriptor->setFormat(NimBLE2904::FORMAT_UINT8);
|
||||
batteryLevelDescriptor->setNamespace(1);
|
||||
batteryLevelDescriptor->setUnit(0x27ad);
|
||||
@@ -912,54 +889,12 @@ void NimbleBluetooth::setupService()
|
||||
batteryService->start();
|
||||
}
|
||||
|
||||
void NimbleBluetooth::startAdvertising()
|
||||
{
|
||||
#ifdef NIMBLE_TWO
|
||||
NimBLEExtAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
NimBLEExtAdvertisement legacyAdvertising;
|
||||
|
||||
legacyAdvertising.setLegacyAdvertising(true);
|
||||
legacyAdvertising.setScannable(true);
|
||||
legacyAdvertising.setConnectable(true);
|
||||
legacyAdvertising.setFlags(BLE_HS_ADV_F_DISC_GEN);
|
||||
if (powerStatus->getHasBattery() == 1) {
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID((uint16_t)0x180f));
|
||||
}
|
||||
legacyAdvertising.setCompleteServices(NimBLEUUID(MESH_SERVICE_UUID));
|
||||
legacyAdvertising.setMinInterval(500);
|
||||
legacyAdvertising.setMaxInterval(1000);
|
||||
|
||||
NimBLEExtAdvertisement legacyScanResponse;
|
||||
legacyScanResponse.setLegacyAdvertising(true);
|
||||
legacyScanResponse.setConnectable(true);
|
||||
legacyScanResponse.setName(getDeviceName());
|
||||
|
||||
if (!pAdvertising->setInstanceData(0, legacyAdvertising)) {
|
||||
LOG_ERROR("BLE failed to set legacyAdvertising");
|
||||
} else if (!pAdvertising->setScanResponseData(0, legacyScanResponse)) {
|
||||
LOG_ERROR("BLE failed to set legacyScanResponse");
|
||||
} else if (!pAdvertising->start(0, 0, 0)) {
|
||||
LOG_ERROR("BLE failed to start legacyAdvertising");
|
||||
}
|
||||
#else
|
||||
NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
|
||||
pAdvertising->reset();
|
||||
pAdvertising->addServiceUUID(MESH_SERVICE_UUID);
|
||||
pAdvertising->addServiceUUID(NimBLEUUID((uint16_t)0x180f)); // 0x180F is the Battery Service
|
||||
pAdvertising->start(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
if ((config.bluetooth.enabled == true) && bleServer && nimbleBluetooth->isConnected()) {
|
||||
BatteryCharacteristic->setValue(&level, 1);
|
||||
#ifdef NIMBLE_TWO
|
||||
BatteryCharacteristic->notify(&level, 1, BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
BatteryCharacteristic->notify();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -974,11 +909,7 @@ void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
if (!bleServer || !isConnected() || length > 512) {
|
||||
return;
|
||||
}
|
||||
#ifdef NIMBLE_TWO
|
||||
logRadioCharacteristic->notify(logMessage, length, BLE_HS_CONN_HANDLE_NONE);
|
||||
#else
|
||||
logRadioCharacteristic->notify(logMessage, length, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
void clearNVS()
|
||||
|
||||
@@ -12,16 +12,11 @@ class NimbleBluetooth : BluetoothApi
|
||||
bool isConnected();
|
||||
int getRssi();
|
||||
void sendLog(const uint8_t *logMessage, size_t length);
|
||||
#if defined(NIMBLE_TWO)
|
||||
void startAdvertising();
|
||||
#endif
|
||||
bool isDeInit = false;
|
||||
|
||||
private:
|
||||
void setupService();
|
||||
#if !defined(NIMBLE_TWO)
|
||||
void startAdvertising();
|
||||
#endif
|
||||
};
|
||||
|
||||
void setBluetoothEnable(bool enable);
|
||||
|
||||
@@ -66,8 +66,6 @@
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO
|
||||
#elif defined(T_ECHO_LITE)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_LITE
|
||||
#elif defined(TTGO_T_ECHO_PLUS)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_PLUS
|
||||
#elif defined(ELECROW_ThinkNode_M1)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M1
|
||||
#elif defined(ELECROW_ThinkNode_M3)
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "target_specific.h"
|
||||
|
||||
#include "PortduinoGlue.h"
|
||||
#include "SHA256.h"
|
||||
#include "api/ServerAPI.h"
|
||||
#include "linux/gpio/LinuxGPIOPin.h"
|
||||
#include "meshUtils.h"
|
||||
@@ -271,39 +270,7 @@ void portduinoSetup()
|
||||
}
|
||||
std::cout << "autoconf: Found Pi HAT+ " << hat_vendor << " " << autoconf_product << " at /proc/device-tree/hat"
|
||||
<< std::endl;
|
||||
|
||||
// potential TODO: Validate that this is a real UUID
|
||||
std::ifstream hatUUID("/proc/device-tree/hat/uuid");
|
||||
char uuid[38] = {0};
|
||||
if (hatUUID.is_open()) {
|
||||
hatUUID.read(uuid, 37);
|
||||
hatUUID.close();
|
||||
std::cout << "autoconf: UUID " << uuid << std::endl;
|
||||
SHA256 uuid_hash;
|
||||
uint8_t uuid_hash_bytes[32] = {0};
|
||||
|
||||
uuid_hash.reset();
|
||||
uuid_hash.update(uuid, 37);
|
||||
uuid_hash.finalize(uuid_hash_bytes, 32);
|
||||
|
||||
for (int j = 0; j < 16; j++) {
|
||||
portduino_config.device_id[j] = uuid_hash_bytes[j];
|
||||
}
|
||||
portduino_config.has_device_id = true;
|
||||
uint8_t dmac[6] = {0};
|
||||
dmac[0] = (uuid_hash_bytes[17] << 4) | 2;
|
||||
dmac[1] = uuid_hash_bytes[18];
|
||||
dmac[2] = uuid_hash_bytes[19];
|
||||
dmac[3] = uuid_hash_bytes[20];
|
||||
dmac[4] = uuid_hash_bytes[21];
|
||||
dmac[5] = uuid_hash_bytes[22];
|
||||
char macBuf[13] = {0};
|
||||
snprintf(macBuf, sizeof(macBuf), "%02X%02X%02X%02X%02X%02X", dmac[0], dmac[1], dmac[2], dmac[3], dmac[4],
|
||||
dmac[5]);
|
||||
portduino_config.mac_address = macBuf;
|
||||
found_hat = true;
|
||||
}
|
||||
|
||||
found_hat = true;
|
||||
} else {
|
||||
std::cout << "autoconf: Could not locate Pi HAT+ at /proc/device-tree/hat" << std::endl;
|
||||
}
|
||||
@@ -312,7 +279,7 @@ void portduinoSetup()
|
||||
// RAK6421-13300-S1:aabbcc123456:5ba85807d92138b7519cfb60460573af:3061e8d8
|
||||
// <model string>:mac address :<16 random unique bytes in hexidecimal> : crc32
|
||||
// crc32 is calculated on the eeprom string up to but not including the final colon
|
||||
if (strlen(autoconf_product) < 6 && portduino_config.i2cdev != "") {
|
||||
if (strlen(autoconf_product) < 6) {
|
||||
try {
|
||||
char *mac_start = nullptr;
|
||||
char *devID_start = nullptr;
|
||||
@@ -399,14 +366,6 @@ void portduinoSetup()
|
||||
cleanupNameForAutoconf("lora-hat-" + std::string(hat_vendor) + "-" + autoconf_product + ".yaml");
|
||||
} else if (found_ch341) {
|
||||
product_config = cleanupNameForAutoconf("lora-usb-" + std::string(autoconf_product) + ".yaml");
|
||||
// look for more data after the null terminator
|
||||
size_t len = strlen(autoconf_product);
|
||||
if (len < 74) {
|
||||
memcpy(portduino_config.device_id, autoconf_product + len + 1, 16);
|
||||
if (!memfll(portduino_config.device_id, '\0', 16) && !memfll(portduino_config.device_id, 0xff, 16)) {
|
||||
portduino_config.has_device_id = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't try to automatically find config for a device with RAK eeprom.
|
||||
@@ -452,11 +411,9 @@ void portduinoSetup()
|
||||
ch341Hal->getProductString(product_string, 95);
|
||||
std::cout << "CH341 Product " << product_string << std::endl;
|
||||
if (strlen(serial) == 8 && portduino_config.mac_address.length() < 12) {
|
||||
std::cout << "Deriving MAC address from Serial and Product String" << std::endl;
|
||||
uint8_t hash[104] = {0};
|
||||
uint8_t hash[32] = {0};
|
||||
memcpy(hash, serial, 8);
|
||||
memcpy(hash + 8, product_string, strlen(product_string));
|
||||
crypto->hash(hash, 8 + strlen(product_string));
|
||||
crypto->hash(hash, 8);
|
||||
dmac[0] = (hash[0] << 4) | 2;
|
||||
dmac[1] = hash[1];
|
||||
dmac[2] = hash[2];
|
||||
@@ -831,6 +788,12 @@ bool loadConfig(const char *configPath)
|
||||
}
|
||||
}
|
||||
|
||||
if (yamlConfig["StoreAndForward"]) {
|
||||
portduino_config.sfpp_stratum0 = (yamlConfig["StoreAndForward"]["Stratum0"]).as<bool>(false);
|
||||
portduino_config.sfpp_initial_sync = (yamlConfig["StoreAndForward"]["InitialSync"]).as<int>(10);
|
||||
portduino_config.sfpp_hops = (yamlConfig["StoreAndForward"]["Hops"]).as<int>(3);
|
||||
}
|
||||
|
||||
if (yamlConfig["General"]) {
|
||||
portduino_config.MaxNodes = (yamlConfig["General"]["MaxNodes"]).as<int>(200);
|
||||
portduino_config.maxtophone = (yamlConfig["General"]["MaxMessageQueue"]).as<int>(100);
|
||||
@@ -912,4 +875,4 @@ void readGPIOFromYaml(YAML::Node sourceNode, pinMapping &destPin, int pinDefault
|
||||
destPin.line = destPin.pin;
|
||||
destPin.gpiochip = portduino_config.lora_default_gpiochip;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,6 +169,19 @@ extern struct portduino_config_struct {
|
||||
int configDisplayMode = 0;
|
||||
bool has_configDisplayMode = false;
|
||||
|
||||
// Store and Forward++
|
||||
// DB location /var/lib/meshtasticd/
|
||||
std::string sfpp_db_path = "/var/lib/meshtasticd/";
|
||||
bool sfpp_stratum0 = false;
|
||||
int sfpp_initial_sync = 10;
|
||||
int sfpp_hops = 3;
|
||||
int sfpp_announce_interval = 5; // minutes
|
||||
uint32_t sfpp_max_chain = 1000;
|
||||
// allowed root hashes
|
||||
// upstream node
|
||||
// Are we allowing unknown channel hashes? Does this even make sense?
|
||||
// Allow DMs
|
||||
|
||||
// General
|
||||
std::string mac_address = "";
|
||||
bool mac_address_explicit = false;
|
||||
@@ -488,6 +501,18 @@ extern struct portduino_config_struct {
|
||||
out << YAML::EndMap; // Config
|
||||
}
|
||||
|
||||
// StoreAndForward
|
||||
if (sfpp_stratum0 || sfpp_initial_sync != 10 || sfpp_hops != 3 || sfpp_announce_interval != 5 || sfpp_max_chain != 1000) {
|
||||
out << YAML::Key << "StoreAndForward" << YAML::Value << YAML::BeginMap;
|
||||
|
||||
out << YAML::Key << "Stratum0" << YAML::Value << sfpp_stratum0;
|
||||
out << YAML::Key << "InitialSync" << YAML::Value << sfpp_initial_sync;
|
||||
out << YAML::Key << "Hops" << YAML::Value << sfpp_hops;
|
||||
out << YAML::Key << "AnnounceInterval" << YAML::Value << sfpp_announce_interval;
|
||||
out << YAML::Key << "MaxChainLength" << YAML::Value << sfpp_max_chain;
|
||||
out << YAML::EndMap; // StoreAndForward
|
||||
}
|
||||
|
||||
// General
|
||||
out << YAML::Key << "General" << YAML::Value << YAML::BeginMap;
|
||||
if (config_directory != "")
|
||||
|
||||
@@ -64,7 +64,7 @@ class Ch341Hal : public RadioLibHal
|
||||
void getProductString(char *_product_string, size_t len)
|
||||
{
|
||||
len = len > 95 ? 95 : len;
|
||||
memcpy(_product_string, pinedio.product_string, len);
|
||||
strncpy(_product_string, pinedio.product_string, len);
|
||||
}
|
||||
|
||||
void init() override {}
|
||||
|
||||
@@ -418,9 +418,8 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi);
|
||||
if (mp->rx_snr != 0)
|
||||
jsonObj["snr"] = new JSONValue((float)mp->rx_snr);
|
||||
const int8_t hopsAway = getHopsAway(*mp);
|
||||
if (hopsAway >= 0) {
|
||||
jsonObj["hops_away"] = new JSONValue((unsigned int)(hopsAway));
|
||||
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) {
|
||||
jsonObj["hops_away"] = new JSONValue((unsigned int)(mp->hop_start - mp->hop_limit));
|
||||
jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start));
|
||||
}
|
||||
|
||||
@@ -451,9 +450,8 @@ std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPa
|
||||
jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi);
|
||||
if (mp->rx_snr != 0)
|
||||
jsonObj["snr"] = new JSONValue((float)mp->rx_snr);
|
||||
const int8_t hopsAway = getHopsAway(*mp);
|
||||
if (hopsAway >= 0) {
|
||||
jsonObj["hops_away"] = new JSONValue((unsigned int)(hopsAway));
|
||||
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) {
|
||||
jsonObj["hops_away"] = new JSONValue((unsigned int)(mp->hop_start - mp->hop_limit));
|
||||
jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start));
|
||||
}
|
||||
jsonObj["size"] = new JSONValue((unsigned int)mp->encrypted.size);
|
||||
|
||||
@@ -358,9 +358,8 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
jsonObj["rssi"] = (int)mp->rx_rssi;
|
||||
if (mp->rx_snr != 0)
|
||||
jsonObj["snr"] = (float)mp->rx_snr;
|
||||
const int8_t hopsAway = getHopsAway(*mp);
|
||||
if (hopsAway >= 0) {
|
||||
jsonObj["hops_away"] = (unsigned int)(hopsAway);
|
||||
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) {
|
||||
jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit);
|
||||
jsonObj["hop_start"] = (unsigned int)(mp->hop_start);
|
||||
}
|
||||
|
||||
@@ -394,9 +393,8 @@ std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPa
|
||||
jsonObj["rssi"] = (int)mp->rx_rssi;
|
||||
if (mp->rx_snr != 0)
|
||||
jsonObj["snr"] = (float)mp->rx_snr;
|
||||
const int8_t hopsAway = getHopsAway(*mp);
|
||||
if (hopsAway >= 0) {
|
||||
jsonObj["hops_away"] = (unsigned int)(hopsAway);
|
||||
if (mp->hop_start != 0 && mp->hop_limit <= mp->hop_start) {
|
||||
jsonObj["hops_away"] = (unsigned int)(mp->hop_start - mp->hop_limit);
|
||||
jsonObj["hop_start"] = (unsigned int)(mp->hop_start);
|
||||
}
|
||||
jsonObj["size"] = (unsigned int)mp->encrypted.size;
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
; Port to Disaster Radio's ESP32-v3 Dev Board
|
||||
[env:meshtastic-dr-dev]
|
||||
custom_meshtastic_hw_model = 41
|
||||
custom_meshtastic_hw_model_slug = DR_DEV
|
||||
custom_meshtastic_architecture = esp32
|
||||
custom_meshtastic_actively_supported = false
|
||||
custom_meshtastic_display_name = DR-DEV
|
||||
custom_meshtastic_tags = DIY
|
||||
|
||||
extends = esp32_base
|
||||
board = esp32doit-devkit-v1
|
||||
board_level = extra
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
; Hydra - Meshtastic DIY v1 hardware with some specific changes
|
||||
[env:hydra]
|
||||
custom_meshtastic_hw_model = 39
|
||||
custom_meshtastic_hw_model_slug = HYDRA
|
||||
custom_meshtastic_architecture = esp32
|
||||
custom_meshtastic_actively_supported = true
|
||||
custom_meshtastic_support_level = 3
|
||||
custom_meshtastic_display_name = Hydra
|
||||
custom_meshtastic_tags = DIY
|
||||
|
||||
extends = esp32_base
|
||||
board = esp32doit-devkit-v1
|
||||
build_flags =
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
; Meshtastic DIY v1 by Nano VHF Schematic based on ESP32-WROOM-32 (38 pins) devkit & EBYTE E22 SX1262/SX1268 module
|
||||
[env:meshtastic-diy-v1]
|
||||
custom_meshtastic_hw_model = 39
|
||||
custom_meshtastic_hw_model_slug = DIY_V1
|
||||
custom_meshtastic_architecture = esp32
|
||||
custom_meshtastic_actively_supported = true
|
||||
custom_meshtastic_support_level = 3
|
||||
custom_meshtastic_display_name = DIY V1
|
||||
custom_meshtastic_images = diy.svg
|
||||
custom_meshtastic_tags = DIY
|
||||
|
||||
extends = esp32_base
|
||||
board = esp32doit-devkit-v1
|
||||
board_check = true
|
||||
|
||||
@@ -38,6 +38,7 @@ build_flags =
|
||||
-DAXP_DEBUG_PORT=Serial
|
||||
-DCONFIG_BT_NIMBLE_ENABLED
|
||||
-DCONFIG_BT_NIMBLE_MAX_BONDS=6 # default is 3
|
||||
-DCONFIG_BT_NIMBLE_ROLE_CENTRAL_DISABLED
|
||||
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
|
||||
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
|
||||
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
|
||||
@@ -60,11 +61,11 @@ lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master
|
||||
https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip
|
||||
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
|
||||
h2zero/NimBLE-Arduino@^1.4.3
|
||||
h2zero/NimBLE-Arduino@2.3.7
|
||||
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
|
||||
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=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
|
||||
lewisxhe/XPowersLib@0.3.2
|
||||
# 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
|
||||
|
||||
@@ -5,4 +5,4 @@ extends = esp32_common
|
||||
custom_esp32_kind = esp32
|
||||
|
||||
build_flags =
|
||||
${esp32_common.build_flags}
|
||||
${esp32_common.build_flags}
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
[env:heltec-v1]
|
||||
custom_meshtastic_hw_model = 11
|
||||
custom_meshtastic_hw_model_slug = HELTEC_V1
|
||||
custom_meshtastic_architecture = esp32
|
||||
custom_meshtastic_actively_supported = false
|
||||
custom_meshtastic_display_name = Heltec V1
|
||||
custom_meshtastic_tags = Heltec
|
||||
|
||||
;build_type = debug ; to make it possible to step through our jtag debugger
|
||||
extends = esp32_base
|
||||
board_level = extra
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
[env:heltec-v2_1]
|
||||
custom_meshtastic_hw_model = 10
|
||||
custom_meshtastic_hw_model_slug = HELTEC_V2_1
|
||||
custom_meshtastic_architecture = esp32
|
||||
custom_meshtastic_actively_supported = false
|
||||
custom_meshtastic_display_name = Heltec V2.1
|
||||
custom_meshtastic_tags = Heltec
|
||||
|
||||
board_level = extra
|
||||
;build_type = debug ; to make it possible to step through our jtag debugger
|
||||
extends = esp32_base
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user