Compare commits

...
Author SHA1 Message Date
Ben Meadors ec5fd4e3d8 Automated version bumps 2026-06-24 07:38:43 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
e3145f9007 Update actions/cache action to v6 (#10771)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-23 20:41:42 -05:00
54e0d8d0ab fix(phone-api): skip manifest scan for node-info-only config requests (#10754)
* fix(fs): bound getFiles() result size and harden traversal

The recursive filesystem traversal in getFiles() could grow its result
vector without limit, contributing to heap pressure on memory-constrained
devices during client config starts. The implementation also relied on
unbounded strcpy and per-entry String() allocations for dot-suffix
filtering, and leaked the directory handle when the opened path was not
a directory.

Rework the traversal to address all of those at once:

- Add optional maxCount (default 64) and wasLimited out-param. The walk
  stops once maxCount entries have been collected and wasLimited is set,
  so callers can distinguish a complete listing from a truncated one.
  Default arguments preserve backward compatibility for existing callers.

- Extract the recursive walk into a collectFiles() helper in an
  anonymous namespace, accumulating into a single caller-owned vector
  instead of building and inserting temporary vectors at every recursion
  level.

- Replace strcpy with strlcpy so file_name writes are bounded by the
  destination buffer.

- Replace String(name).endsWith(".") with a stack-based
  pathEndsWithDot() helper to avoid a heap allocation per entry.

- Close the directory handle when the opened path is not a directory,
  and close the file in both branches of recursion instead of only the
  leaf branch (the previous code leaked the handle when depth was
  exhausted).

- Compile-gate the std::bad_alloc reserve probe-down behind
  __cpp_exceptions / __EXCEPTIONS so the fallback only compiles where
  bad_alloc is actually catchable. No-exception targets (nRF52, RP2040,
  STM32, ESP32-C6) skip the probe and rely on the bounded maxCount
  alone, which is safe because the default cap is small enough to fit
  comfortably in available RAM on every supported platform.

* fix(phone-api): skip manifest scan for node-info-only config requests

A client can request a node-info-only config response by sending
want_config_id with the SPECIAL_NONCE_ONLY_NODES nonce. The manifest
walk in handleStartConfig() ran unconditionally even though
STATE_SEND_FILEMANIFEST short-circuits past the manifest on that path,
so the SPI lock and recursive filesystem traversal were wasted work.
On memory-constrained devices this extra heap pressure was enough to
trigger allocation aborts and spontaneous reboots before the node-info
response could complete.

Gate the manifest call behind config_nonce != SPECIAL_NONCE_ONLY_NODES
so the node-info-only path no longer touches the filesystem at all, and
release any stale manifest storage left over from a previous run.

For normal full-config requests, also tighten the scan limits from
depth 10 / unbounded to depth 3 / max 64 entries via the new getFiles()
parameters. A 10-level recursive listing was never useful for a client
file picker and amplified heap pressure for no benefit. Log a warning
when the listing was truncated so operators can distinguish a bounded
result from a genuinely empty filesystem.

A few small cleanups fall out of the above:

- Replace raw spiLock->lock()/unlock() with a LockGuard so the lock is
  released even if getFiles() throws on exception-enabled targets.

- Use a releaseFilesManifest() helper (swap-with-empty idiom) in
  close() and the STATE_SEND_FILEMANIFEST drain path instead of the
  previous clear()+shrink_to_fit() pair. Equivalent behavior, but the
  swap idiom is the canonical vector-freeing pattern and centralizing
  it avoids divergence between call sites.

- Use %zu for the size_t log arguments.

* fix(fs): harden bounded directory traversal

Close each child directory entry before recursive descent so bounded manifest scans do not stack file handles on constrained filesystems.

Treat null or overlong paths as truncated listings, handle reserve length failures like allocation failures, and keep manifest constants aligned with repository naming.

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-23 11:26:52 -05:00
640002dc93 Upgrade trunk (#10763)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2026-06-23 06:32:17 -05:00
CopilotGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>Ben Meadors
e4c1374b99 fix(FSCommon): add nRF52 LittleFS empty-name guard to getFiles() (#10757)
* Initial plan

* fix(FSCommon): add file.name()[0] guard in getFiles() for nRF52 LittleFS bug workaround

* style(FSCommon): run clang-format (trunk fmt)

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-06-23 06:23:53 -05:00
Ben Meadors 22d08b4303 Fix build on picomputer 2026-06-23 06:03:37 -05:00
560515f014 Upgrade trunk (#10760)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2026-06-22 06:22:04 -05:00
ManuelGitHubcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
4044f4af6c tdeck touch driver fix (#10740)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-06-20 13:15:11 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
3b20601697 Update meshtastic/device-ui digest to 1c45ebc (#10749)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-19 19:56:04 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
a9a88e7ac7 Update platform-native digest to 61067ac (#10750)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-19 19:55:49 -05:00
Jonathan BennettandGitHub 20c9e154dd enable Adafruit SHTC3 library on native (#10747) 2026-06-19 12:52:10 -05:00
e08b67aa8c Upgrade trunk (#10738)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2026-06-19 06:18:41 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
df3d5bb344 Update actions/checkout action to v7 (#10744)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-19 06:18:09 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
804ec0fb1b Update meshtastic/device-ui digest to 6cadf54 (#10743)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-19 06:17:41 -05:00
8975181bd0 fix(esp32): skip RTC timer wake on user shutdown; TAP V2 OCV and partition (#10739)
* fix(esp32): skip RTC timer wake on user shutdown

Do not arm esp_sleep_enable_timer_wakeup when msecToWake is portMAX_DELAY (UI shutdown), matching nRF52 system_off semantics.

fix(rak_wismesh_tap_v2): Tag OCV curve and 16MB partition

Add OCV_ARRAY matching WisMesh Tag for accurate SOC. Use 16MB flash partition scheme for TAP V2 hardware.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Trunkt

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-06-18 16:30:41 -05:00
Jonathan BennettandGitHub 1003d154b2 Update OCV_ARRAY values in Thinknode m5 variant.h 2026-06-17 22:16:09 -05:00
Jonathan BennettandGitHub 8ba9d0d15f Set the time from GPS every 30 minutes (#10737) 2026-06-17 18:46:58 -05:00
ca1304e6da Upgrade trunk (#10720)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2026-06-17 05:54:26 -05:00
Jonathan BennettandGitHub 40adf3a0d0 Beta fixes (#10728)
* Wipe message Store on factory reset

* Check for destination 0 in a new message, and convert to broadcast

* Make sure CHARGE_LED_state gets turned off, to avoid stuck LEDs

* Take the spiLock in MessageStore when clearing messages

* Trunk

* Add thinknode M5 voltage curve

* Fix the oops
2026-06-16 22:37:14 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
ac25b85f5b Update meshtastic/device-ui digest to 4ec010c (#10732)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-16 20:21:11 -05:00
a49729eb89 Additional *RAK* missing values for 6421 / 13300 13302 modules + Zebra/Nebra Hat Configs (#10644)
* rebased to master

* Update bin/config.d/lora-ZebraHat_2W.yaml

Co-authored-by: Austin <vidplace7@gmail.com>

* Update bin/config.d/lora-ZebraHat_1W.yaml

Co-authored-by: Austin <vidplace7@gmail.com>

* Update bin/config.d/lora-NebraHat_2W.yaml

Co-authored-by: Austin <vidplace7@gmail.com>

* Update bin/config.d/lora-NebraHat_1W.yaml

Co-authored-by: Austin <vidplace7@gmail.com>

* Remove TX_GAIN_LORA configuration line

* Remove TX_GAIN_LORA configuration line

* Comment out TX_GAIN_LORA configuration

* Comment out TX_GAIN_LORA configuration

* Update lora-ok3506-RAK6421-13300-slot1.yaml

* Update lora-ok3506-RAK6421-13300-slot2.yaml

* Update lora-RAK6421-13300-slot1.yaml

* Update lora-RAK6421-13300-slot2.yaml

---------

Co-authored-by: Austin <vidplace7@gmail.com>
2026-06-16 11:22:45 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
6e02b9aed9 Update Sensirion I2C SCD30 to v1.1.1 (#10294)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-14 07:08:28 -05:00
9972feb5bb Lora led rx (#10674)
* add optional LED_LORA to indicate LoRa TX

* Briefly flash LED_LORA on packet RX

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-06-13 12:06:06 -05:00
c9398cccaa Use standard GPS enable pin, for smarter power control on M3 (#10671)
* Use standard GPS enable pin, for smarter power control on M3

* Enable GPS pin in variant.cpp initialization

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-06-13 12:04:41 -05:00
88137c60e6 Upgrade trunk (#10701)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2026-06-12 05:57:51 -05:00
4e7181c798 Upgrade trunk (#10621)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2026-06-11 08:26:34 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
06db0f1b51 Update actions/github-script action to v9 (#10691)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-11 06:53:44 -05:00
Ben Meadors fcff9af16c Add placeholder comment for web flasher during PR builds 2026-06-10 17:27:58 -05:00
Ben Meadors dfae28895a fix(workflows): update artifact selection to exclude expired firmware size artifacts 2026-06-10 13:23:58 -05:00
Ben Meadors 6b3b2630f8 Flasher link fix 2026-06-10 13:00:54 -05:00
bfc206a47b meshtasticd: Add configs for B&Q Station G3 (#10673)
configs for raspberry pi and luckfox lyra zero

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2026-06-10 12:59:55 -05:00
Ben Meadors f29f32bfba Restrict web flasher link comments to organization members only 2026-06-10 06:35:18 -05:00
Ben Meadors 9eff900f8e Add GitHub Action to post web flasher link comments on successful PR workflows 2026-06-10 06:35:05 -05:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
3471c0e6fe Update alpine Docker tag to v3.24 (#10668)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-09 20:40:48 -05:00
49c5ad3875 Automated version bumps (#10667)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2026-06-09 20:07:45 -05:00
104df5f9e5 Update protobufs (#10654)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2026-06-08 16:20:05 -05:00
Jonathan BennettandBen Meadors 7440cd54bb Update nanopb download URL in workflow 2026-06-08 14:59:01 -05:00
AustinandGitHub 5c769023af Actions: Update protobufs using the triggering branch (#10612) 2026-06-08 14:53:12 -05:00
75 changed files with 952 additions and 197 deletions
+1 -1
View File
@@ -76,7 +76,7 @@ runs:
done
- name: PlatformIO ${{ inputs.arch }} download cache
uses: actions/cache@v5
uses: actions/cache@v6
with:
path: ~/.platformio/.cache
key: pio-cache-${{ inputs.arch }}-${{ hashFiles('.github/actions/**', '**.ini') }}
+1 -1
View File
@@ -5,7 +5,7 @@ runs:
using: composite
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
submodules: recursive
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
submodules: recursive
path: meshtasticd
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
outputs:
artifact-id: ${{ steps.upload-firmware.outputs.artifact-id }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
submodules: recursive
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: macos-${{ inputs.macos_ver }}
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
submodules: recursive
+3 -3
View File
@@ -43,7 +43,7 @@ jobs:
- stm32
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: 3.x
@@ -64,7 +64,7 @@ jobs:
if: ${{ inputs.target != '' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -93,7 +93,7 @@ jobs:
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
runs-on: ${{ inputs.runs-on }}
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
submodules: recursive
+1 -1
View File
@@ -103,7 +103,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
submodules: recursive
+190
View File
@@ -0,0 +1,190 @@
name: Post Web Flasher Link Comment
on:
workflow_run:
workflows: [CI]
types: [completed]
permissions:
pull-requests: write
actions: read
jobs:
post-flasher-link:
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion != 'cancelled' &&
github.repository == 'meshtastic/firmware'
continue-on-error: true
runs-on: ubuntu-latest
steps:
# Per-board manifests carry the firmware's own metadata (activelySupported,
# displayName, ...) generated from each target's custom_meshtastic_* config.
- name: Download board manifests
uses: actions/download-artifact@v8
continue-on-error: true
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
pattern: manifest-*
path: ./manifests
merge-multiple: true
- name: Post or update web flasher link comment
uses: actions/github-script@v9
with:
script: |
const marker = '<!-- web-flasher-link -->';
const run = context.payload.workflow_run;
const { owner, repo } = context.repo;
// Resolve the PR number (run.pull_requests is empty for fork PRs)
let prNumber = run.pull_requests?.[0]?.number;
if (!prNumber) {
const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner, repo, commit_sha: run.head_sha,
});
prNumber = (prs.find((pr) => pr.head.sha === run.head_sha) ?? prs[0])?.number;
}
if (!prNumber) {
core.info('No pull request associated with this run; skipping.');
return;
}
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: prNumber });
// Only comment on PRs authored by members of the organization.
// author_association MEMBER is computed by GitHub and reflects org
// membership (including concealed members); OWNER covers a repo owner.
const allowedAssociations = ['OWNER', 'MEMBER'];
if (!allowedAssociations.includes(pr.author_association)) {
core.info(`Author association ${pr.author_association} is not an org member; skipping.`);
return;
}
if (pr.state !== 'open') {
core.info('Pull request is not open; skipping.');
return;
}
if (pr.head.sha !== run.head_sha) {
core.info('Run is for an outdated commit; skipping.');
return;
}
// Require at least one per-arch firmware artifact from gather-artifacts
const artifacts = await github.paginate(github.rest.actions.listWorkflowRunArtifacts, {
owner, repo, run_id: run.id, per_page: 100,
});
const archRe = /^firmware-(esp32|esp32s3|esp32c3|esp32c6|nrf52840|rp2040|rp2350|stm32)-(\d+\.\d+\.\d+\.[0-9a-f]+)$/;
const archArtifacts = artifacts.filter((a) => archRe.test(a.name) && !a.expired);
if (archArtifacts.length === 0) {
core.info('No per-arch firmware artifacts found; skipping.');
return;
}
const version = archRe.exec(archArtifacts[0].name)[2];
const expiresAt = archArtifacts[0].expires_at
? new Date(archArtifacts[0].expires_at).toISOString().slice(0, 10)
: null;
// Read each built board's manifest (.mt.json). activelySupported,
// displayName and architecture come straight from the board's
// custom_meshtastic_* platformio config, so the list is in sync with
// the firmware itself — no external device database needed.
const fs = require('fs');
let boards = [];
try {
boards = fs.readdirSync('./manifests')
.filter((f) => f.endsWith('.mt.json'))
.map((f) => {
try { return JSON.parse(fs.readFileSync(`./manifests/${f}`, 'utf8')); }
catch { return null; }
})
.filter((m) => m && m.activelySupported === true && m.platformioTarget)
.map((m) => ({
board: m.platformioTarget,
platform: m.architecture || '',
// displayName is maintainer-authored text; escape table-breaking pipes
displayName: String(m.displayName || m.platformioTarget).replace(/\|/g, '\\|'),
image: Array.isArray(m.images) && m.images[0] ? String(m.images[0]) : '',
}))
.sort((a, b) => a.board.localeCompare(b.board));
} catch (e) {
core.warning(`Could not read board manifests: ${e.message}`);
}
const flasherUrl = `https://flasher.meshtastic.org/?pr=${prNumber}`;
// Device illustrations are served by the flasher from the same image
// names the manifest declares (custom_meshtastic_images). The flasher
// serves its SPA shell (HTML, 200) for unknown paths, so confirm each
// image really resolves to an image before linking it.
const imageBase = 'https://flasher.meshtastic.org/img/devices/';
await Promise.all(boards.map(async (b) => {
if (!b.image) return;
try {
const res = await fetch(`${imageBase}${encodeURIComponent(b.image)}`);
const type = res.headers.get('content-type') || '';
if (!res.ok || !type.startsWith('image/')) b.image = '';
} catch { b.image = ''; }
}));
const boardLines = boards
.map((b) => {
const img = b.image ? `<img src="${imageBase}${encodeURIComponent(b.image)}" alt="" height="34">` : '';
return `| ${img} | ${b.displayName} | [\`${b.board}\`](${flasherUrl}&device=${encodeURIComponent(b.board)}) | ${b.platform} |`;
})
.join('\n');
// Shields.io badges. Only non-user-controlled, charset-constrained values
// (version, commit sha, counts, dates) go into badge URLs — never board
// names or the PR title — so the rendered comment cannot be spoofed.
const shieldText = (s) =>
encodeURIComponent(String(s).replace(/-/g, '--').replace(/_/g, '__').replace(/ /g, '_'));
const shield = (label, message, color) =>
`https://img.shields.io/badge/${shieldText(label)}-${shieldText(message)}-${color}`;
const buttonUrl =
`https://img.shields.io/badge/${shieldText('Flash this PR in the Web Flasher')}-2C2D3C?style=for-the-badge`;
const badges = [
`![firmware](${shield('firmware', version, '67EA94')})`,
`![commit](${shield('commit', run.head_sha.slice(0, 7), '2C2D3C')})`,
`![boards](${shield('boards', boards.length, '5C6BC0')})`,
];
if (expiresAt) badges.push(`![expires](${shield('expires', expiresAt, '9A4E00')})`);
// Only render the board table when there are supported boards to list
const boardTable = boards.length > 0 ? [
`<details><summary>Supported boards built by this PR (${boards.length})</summary>`,
'',
'| | Device | Board | Platform |',
'| --- | --- | --- | --- |',
boardLines,
'',
'</details>',
'',
] : [];
const body = [
marker,
'## ⚡ Try this PR in the Web Flasher',
'',
`[![Flash this PR in the Web Flasher](${buttonUrl})](${flasherUrl})`,
'',
badges.join(' '),
'',
'> [!WARNING]',
'> This is an automated, unreviewed CI test build. Back up your device configuration',
'> before flashing, and only flash devices you are able to recover.',
'',
...boardTable,
`*Build artifacts expire${expiresAt ? ` on ${expiresAt}` : ' after 30 days'}. Updated for \`${run.head_sha.slice(0, 7)}\`.*`,
].join('\n');
// Sticky comment: update in place when the marker is found
const comments = await github.paginate(github.rest.issues.listComments, {
owner, repo, issue_number: prNumber, per_page: 100,
});
const existing = comments.find((c) => c.body?.includes(marker));
if (existing) {
await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body });
} else {
await github.rest.issues.createComment({ owner, repo, issue_number: prNumber, body });
}
@@ -0,0 +1,60 @@
name: Post Web Flasher Build Placeholder
# Drops an immediate "build in progress" comment when a PR opens, so the web
# flasher entry shows up right away. The real CI-driven workflow
# (flasher-link-comment.yml) later replaces it in place via the shared marker.
#
# SECURITY: this uses pull_request_target (write token, runs for fork PRs) but is
# safe because it never checks out or runs PR code and posts a fully static body
# — no PR title, branch name, or other untrusted input is used anywhere.
on:
pull_request_target:
types: [opened, reopened]
permissions:
pull-requests: write
jobs:
post-placeholder:
if: github.repository == 'meshtastic/firmware'
continue-on-error: true
runs-on: ubuntu-latest
steps:
- name: Post web flasher build-in-progress placeholder
uses: actions/github-script@v9
with:
script: |
const marker = '<!-- web-flasher-link -->';
const { owner, repo } = context.repo;
const pr = context.payload.pull_request;
// Only org members get the flasher comment (matches the real workflow)
const allowedAssociations = ['OWNER', 'MEMBER'];
if (!allowedAssociations.includes(pr.author_association)) {
core.info(`Author association ${pr.author_association} is not an org member; skipping.`);
return;
}
// Only seed a placeholder when no flasher comment exists yet — never
// overwrite a real (or existing placeholder) comment.
const comments = await github.paginate(github.rest.issues.listComments, {
owner, repo, issue_number: pr.number, per_page: 100,
});
if (comments.some((c) => c.body?.includes(marker))) {
core.info('Flasher comment already exists; nothing to do.');
return;
}
const body = [
marker,
'## ⚡ Try this PR in the Web Flasher',
'',
'> [!NOTE]',
'> Building this pull request… the flash button, badges and supported-board',
'> list will appear here automatically once CI finishes.',
].join('\n');
await github.rest.issues.createComment({
owner, repo, issue_number: pr.number, body,
});
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
submodules: recursive
+7 -49
View File
@@ -40,7 +40,7 @@ jobs:
- check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: 3.x
@@ -64,7 +64,7 @@ jobs:
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -86,7 +86,7 @@ jobs:
runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }}
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
submodules: recursive
- name: Check ${{ matrix.check.board }}
@@ -189,7 +189,7 @@ jobs:
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
@@ -245,48 +245,6 @@ jobs:
path: ./*.elf
retention-days: 30
shame:
if: github.repository == 'meshtastic/firmware'
continue-on-error: true
runs-on: ubuntu-latest
needs: [build]
steps:
- uses: actions/checkout@v6
if: github.event_name == 'pull_request'
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
- name: Download the current manifests
uses: actions/download-artifact@v8
with:
path: ./manifests-new/
pattern: manifest-*
merge-multiple: true
- name: Upload combined manifests for later commit and global stats crunching.
uses: actions/upload-artifact@v7
id: upload-manifest
with:
name: manifests-${{ github.sha }}
overwrite: true
path: manifests-new/*.mt.json
- name: Find the merge base
if: github.event_name == 'pull_request'
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'
# 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'
# run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/
release-artifacts:
permissions: # Needed for 'gh release upload'.
contents: write
@@ -303,7 +261,7 @@ jobs:
# - MacOS
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
@@ -404,7 +362,7 @@ jobs:
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Setup Python
uses: actions/setup-python@v6
@@ -459,7 +417,7 @@ jobs:
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
+2 -2
View File
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Trunk Check
uses: trunk-io/trunk-action@v1.3.1
@@ -31,7 +31,7 @@ jobs:
pull-requests: write # For trunk to create PRs
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Trunk Upgrade
uses: trunk-io/trunk-action/upgrade@v1.3.1
+1 -1
View File
@@ -33,7 +33,7 @@ jobs:
needs: build-debian-src
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
submodules: recursive
path: meshtasticd
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
submodules: recursive
+1 -1
View File
@@ -34,7 +34,7 @@ jobs:
needs: build-debian-src
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
submodules: recursive
path: meshtasticd
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
checks: write
pull-requests: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
submodules: recursive
+1 -1
View File
@@ -91,7 +91,7 @@ jobs:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
# Always use master branch for version bumps
ref: master
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
steps:
# step 1
- name: clone application source code
uses: actions/checkout@v6
uses: actions/checkout@v7
# step 2
- name: full scan
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
steps:
# step 1
- name: clone application source code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
fetch-depth: 0
+3 -3
View File
@@ -14,7 +14,7 @@ jobs:
name: Native Simulator Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
submodules: recursive
@@ -68,7 +68,7 @@ jobs:
name: Native PlatformIO Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
submodules: recursive
@@ -123,7 +123,7 @@ jobs:
- platformio-tests
if: always()
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- name: Get release version string
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
runs-on: test-runner
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
# - uses: actions/setup-python@v6
# with:
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Trunk Check
uses: trunk-io/trunk-action@v1.3.1
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Trunk Check
uses: trunk-io/trunk-action@v1.3.1
+10 -5
View File
@@ -11,18 +11,23 @@ jobs:
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
submodules: true
- name: Update submodule
if: ${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' }}
if: ${{ github.ref_name == 'master' || github.ref_name == 'develop' }}
working-directory: protobufs
env:
# Use the branch that triggered the workflow as the protobuf branch.
GIT_BRANCH: ${{ github.ref_name }}
run: |
git submodule update --remote protobufs
git fetch --prune origin $GIT_BRANCH
git checkout origin/$GIT_BRANCH
- name: Download nanopb
run: |
wget https://jpa.kapsi.fi/nanopb/download/nanopb-0.4.9.1-linux-x86.tar.gz
wget https://github.com/nanopb/nanopb/releases/download/nanopb-0.4.9.1/nanopb-0.4.9.1-linux-x86.tar.gz
tar xvzf nanopb-0.4.9.1-linux-x86.tar.gz
mv nanopb-0.4.9.1-linux-x86 nanopb-0.4.9
@@ -33,7 +38,7 @@ jobs:
- name: Create pull request
uses: peter-evans/create-pull-request@v8
with:
branch: create-pull-request/update-protobufs
branch: create-pull-request/update-protobufs-${{ github.ref_name }}
labels: submodules
title: Update protobufs and classes
commit-message: Update protobufs
+8 -8
View File
@@ -4,21 +4,21 @@ cli:
plugins:
sources:
- id: trunk
ref: v1.10.0
ref: v1.10.2
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- checkov@3.2.532
- renovate@43.209.1
- prettier@3.8.3
- trufflehog@3.95.5
- checkov@3.3.1
- renovate@43.236.0
- prettier@3.8.4
- trufflehog@3.95.6
- yamllint@1.38.0
- bandit@1.9.4
- trivy@0.71.0
- trivy@0.71.2
- taplo@0.10.0
- ruff@0.15.15
- ruff@0.15.18
- isort@8.0.1
- markdownlint@0.48.0
- markdownlint@0.49.0
- oxipng@10.1.1
- svgo@4.0.1
- actionlint@1.7.12
+2 -2
View File
@@ -4,7 +4,7 @@
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
# Ensure the Alpine version is updated in both stages of the container!
FROM alpine:3.23 AS builder
FROM alpine:3.24 AS builder
ARG PIO_ENV=native
# Enable Alpine community repository (for 'py3-grpcio-tools')
@@ -35,7 +35,7 @@ RUN bash ./bin/build-native.sh "$PIO_ENV" && \
# ##### PRODUCTION BUILD #############
FROM alpine:3.23
FROM alpine:3.24
LABEL org.opencontainers.image.title="Meshtastic" \
org.opencontainers.image.description="Alpine Meshtastic daemon" \
org.opencontainers.image.url="https://meshtastic.org" \
+19
View File
@@ -0,0 +1,19 @@
# https://github.com/wehooper4/Meshtastic-Hardware/tree/main/NebraHat
# Use for 1 watt hat
Meta:
name: NebraHat 1W
support: community
compatible:
- raspberry-pi
Lora:
Module: sx1262 # Nebra SX1262 Pi Hat - 1W
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true
# CS: 8 # Newer version of MeshtasticD do not need this? If issues uncomment this line
IRQ: 22
Busy: 4
Reset: 18
RXen: 25
I2C:
I2CDevice: /dev/i2c-1
+20
View File
@@ -0,0 +1,20 @@
# https://github.com/wehooper4/Meshtastic-Hardware/tree/main/NebraHat
# Use for 2 watt hat
Meta:
name: NebraHat 2W
support: community
compatible:
- raspberry-pi
Lora:
Module: sx1262 # Nebra SX1262 Pi Hat - 2W
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true
SX126X_MAX_POWER: 8
# CS: 8 # Newer version of MeshtasticD do not need this? If issues uncomment this line
IRQ: 22
Busy: 4
Reset: 18
RXen: 25
I2C:
I2CDevice: /dev/i2c-1
+1 -1
View File
@@ -18,4 +18,4 @@ Lora:
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
# CS: 8
# CS: 8
+1 -1
View File
@@ -18,4 +18,4 @@ Lora:
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.1
# CS: 7
# CS: 7
+19
View File
@@ -0,0 +1,19 @@
# https://github.com/wehooper4/Meshtastic-Hardware/tree/main/ZebraHAT
# Use for 1 watt hat
Meta:
name: ZebraHat 1W
support: community
compatible:
- raspberry-pi
Lora:
Module: sx1262 # Zebra SX1262 Pi Hat - 1W
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true
SX126X_MAX_POWER: 18
CS: 24
IRQ: 22
Busy: 27
Reset: 17
I2C:
I2CDevice: /dev/i2c-1
+20
View File
@@ -0,0 +1,20 @@
# https://github.com/wehooper4/Meshtastic-Hardware/tree/main/ZebraHAT
# Use for 2 watt hat
Meta:
name: ZebraHat 2W
support: community
compatible:
- raspberry-pi
Lora:
Module: sx1262 # Zebra SX1262 Pi Hat - 2W
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true
SX126X_MAX_POWER: 8
CS: 24
IRQ: 22
Busy: 27
Reset: 17
RXen: 25
I2C:
I2CDevice: /dev/i2c-1
@@ -29,6 +29,8 @@ Lora:
- pin: 50 # GPIO1_C2 (physical 16)
gpiochip: 1
line: 18
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.1
# CS: # GPIO0_A7 (SPI1_CSN1, physical 26)
# pin: 7
@@ -29,6 +29,8 @@ Lora:
- pin: 50 # GPIO1_C2 (physical 16)
gpiochip: 1
line: 18
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.1
# CS: # GPIO0_A7 (SPI1_CSN1, physical 26)
# pin: 7
@@ -0,0 +1,29 @@
# Station G3 + BQ35LORA900V1M Primary Slot
# Board Doc: https://wiki.bqvoy.com/en/devkits/station-g3
Meta:
name: B&Q Station G3 + BQ35LORA900V1M Primary Slot
support: official
compatible:
- luckfox-lyra-zero-w # Armbian
Lora:
Module: sx1262
IRQ: # GPIO0_A5 (physical 15)
pin: 5
gpiochip: 0
line: 5
Reset: # GPIO1_D1 (physical 36)
pin: 57
gpiochip: 1
line: 25
Busy: # GPIO0_B4 (physical 18)
pin: 12
gpiochip: 0
line: 12
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
# CS: # GPIO0_B2 (physical 24)
# pin: 10
# gpiochip: 0
# line: 10
@@ -29,6 +29,8 @@ Lora:
- pin: 13 # GPIO0_B5
gpiochip: 0
line: 13
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.1
# CS: # GPIO0_B1
# pin: 9
@@ -29,6 +29,8 @@ Lora:
- pin: 13 # GPIO0_B5
gpiochip: 0
line: 13
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.1
# CS: # GPIO0_B1
# pin: 9
@@ -31,6 +31,8 @@ Lora:
- pin: 103 # GPIO3_A7 (physical 16)
gpiochip: 3
line: 7
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.1
# CS: # GPIO0_B7 (SPI0_CSN1, physical 26)
# pin: 15
@@ -31,6 +31,8 @@ Lora:
- pin: 103 # GPIO3_A7 (physical 16)
gpiochip: 3
line: 7
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.1
# CS: # GPIO0_B7 (SPI0_CSN1, physical 26)
# pin: 15
@@ -0,0 +1,17 @@
# Station G3 + BQ35LORA900V1M Primary Slot
# Board Doc: https://wiki.bqvoy.com/en/devkits/station-g3
Meta:
name: B&Q Station G3 + BQ35LORA900V1M Primary Slot
support: official
compatible:
- raspberry-pi
Lora:
Module: sx1262
IRQ: 22
Reset: 16
Busy: 24
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
#CS: 8
+2
View File
@@ -1,3 +1,5 @@
# This config works with all revisions of the Meshtoad USB radio.
Meta:
name: meshtoad-e22
support: official
@@ -87,6 +87,12 @@
</screenshots>
<releases>
<release version="2.7.27" date="2026-06-24">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.27</url>
</release>
<release version="2.7.26" date="2026-06-10">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.26</url>
</release>
<release version="2.7.25" date="2026-05-23">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.25</url>
</release>
+12
View File
@@ -1,3 +1,15 @@
meshtasticd (2.7.27.0) unstable; urgency=medium
* Version 2.7.27
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 24 Jun 2026 11:20:05 +0000
meshtasticd (2.7.26.0) unstable; urgency=medium
* Version 2.7.26
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 10 Jun 2026 00:19:23 +0000
meshtasticd (2.7.25.0) unstable; urgency=medium
* Version 2.7.25
+4 -4
View File
@@ -126,7 +126,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/502ba30dbccc6a323f1d4f7ca961131c27f60672.zip
https://github.com/meshtastic/device-ui/archive/1c45ebc7433acb8ba3fe96a6f7deca9c43fa54cf.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]
@@ -199,6 +199,8 @@ lib_deps =
https://github.com/adafruit/Adafruit_TSL2561/archive/refs/tags/1.1.3.zip
# renovate: datasource=github-tags depName=BH1750_WE packageName=wollewald/BH1750_WE
https://github.com/wollewald/BH1750_WE/archive/refs/tags/1.1.10.zip
# renovate: datasource=github-tags depName=Adafruit SHTC3 packageName=adafruit/Adafruit_SHTC3
https://github.com/adafruit/Adafruit_SHTC3/archive/refs/tags/1.0.2.zip
; Common environmental sensor libraries (not included in native / portduino)
[environmental_extra_common]
@@ -207,8 +209,6 @@ lib_deps =
https://github.com/adafruit/Adafruit_BMP3XX/archive/refs/tags/2.1.6.zip
# renovate: datasource=github-tags depName=Adafruit MAX1704X packageName=adafruit/Adafruit_MAX1704X
https://github.com/adafruit/Adafruit_MAX1704X/archive/refs/tags/1.0.3.zip
# renovate: datasource=github-tags depName=Adafruit SHTC3 packageName=adafruit/Adafruit_SHTC3
https://github.com/adafruit/Adafruit_SHTC3/archive/refs/tags/1.0.2.zip
# renovate: datasource=github-tags depName=Adafruit LPS2X packageName=adafruit/Adafruit_LPS2X
https://github.com/adafruit/Adafruit_LPS2X/archive/refs/tags/2.0.6.zip
# renovate: datasource=github-tags depName=Adafruit SHT31 packageName=adafruit/Adafruit_SHT31
@@ -230,7 +230,7 @@ lib_deps =
# renovate: datasource=github-tags depName=Sensirion I2C SFA3x packageName=sensirion/arduino-i2c-sfa3x
https://github.com/Sensirion/arduino-i2c-sfa3x/archive/refs/tags/1.0.0.zip
# renovate: datasource=github-tags depName=Sensirion I2C SCD30 packageName=sensirion/arduino-i2c-scd30
https://github.com/Sensirion/arduino-i2c-scd30/archive/refs/tags/1.0.0.zip
https://github.com/Sensirion/arduino-i2c-scd30/archive/1.1.1.zip
; Environmental sensors with BSEC2 (Bosch proprietary IAQ)
[environmental_extra]
+101 -27
View File
@@ -99,48 +99,87 @@ bool renameFile(const char *pathFrom, const char *pathTo)
#endif
}
#include <cstring>
#include <new>
#include <stdexcept>
#include <vector>
/**
* @brief Get the list of files in a directory.
*
* This function returns a list of files in a directory. The list includes the full path of each file.
* We can't use SPILOCK here because of recursion. Callers of this function should use SPILOCK.
*
* @param dirname The name of the directory.
* @param levels The number of levels of subdirectories to list.
* @return A vector of strings containing the full path of each file in the directory.
*/
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels)
{
std::vector<meshtastic_FileInfo> filenames = {};
#ifdef FSCom
namespace
{
bool pathEndsWithDot(const char *path)
{
if (!path)
return false;
size_t length = strlen(path);
return length > 0 && path[length - 1] == '.';
}
bool copyFilePath(char *dest, size_t destSize, const char *path, bool *wasLimited)
{
if (!path || destSize == 0) {
if (wasLimited)
*wasLimited = true;
return false;
}
if (strlcpy(dest, path, destSize) >= destSize) {
if (wasLimited)
*wasLimited = true;
return false;
}
return true;
}
void collectFiles(const char *dirname, uint8_t levels, size_t maxCount, std::vector<meshtastic_FileInfo> &filenames,
bool *wasLimited)
{
if (!dirname)
return;
File root = FSCom.open(dirname, FILE_O_READ);
if (!root)
return filenames;
if (!root.isDirectory())
return filenames;
return;
if (!root.isDirectory()) {
root.close();
return;
}
File file = root.openNextFile();
while (file) {
if (file.isDirectory() && !String(file.name()).endsWith(".")) {
if (levels) {
// file.name()[0] check is a workaround for a bug in the Adafruit LittleFS nrf52 glue (see issue 4395)
while (file && file.name()[0]) {
if (filenames.size() >= maxCount) {
if (wasLimited)
*wasLimited = true;
file.close();
break;
}
const char *fileName = file.name();
if (file.isDirectory() && !pathEndsWithDot(fileName)) {
char pathBuffer[sizeof(((meshtastic_FileInfo *)nullptr)->file_name)] = {};
#ifdef ARCH_ESP32
std::vector<meshtastic_FileInfo> subDirFilenames = getFiles(file.path(), levels - 1);
const char *subDirPath = file.path();
#else
std::vector<meshtastic_FileInfo> subDirFilenames = getFiles(file.name(), levels - 1);
const char *subDirPath = fileName;
#endif
filenames.insert(filenames.end(), subDirFilenames.begin(), subDirFilenames.end());
file.close();
bool hasSubDirPath = copyFilePath(pathBuffer, sizeof(pathBuffer), subDirPath, wasLimited);
file.close();
if (levels && hasSubDirPath) {
collectFiles(pathBuffer, levels - 1, maxCount, filenames, wasLimited);
} else if (wasLimited) {
*wasLimited = true;
}
} else {
meshtastic_FileInfo fileInfo = {"", static_cast<uint32_t>(file.size())};
#ifdef ARCH_ESP32
strcpy(fileInfo.file_name, file.path());
bool hasFilePath = copyFilePath(fileInfo.file_name, sizeof(fileInfo.file_name), file.path(), wasLimited);
#else
strcpy(fileInfo.file_name, file.name());
bool hasFilePath = copyFilePath(fileInfo.file_name, sizeof(fileInfo.file_name), file.name(), wasLimited);
#endif
if (!String(fileInfo.file_name).endsWith(".")) {
if (hasFilePath && !pathEndsWithDot(fileInfo.file_name)) {
filenames.push_back(fileInfo);
}
file.close();
@@ -148,6 +187,41 @@ std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels)
file = root.openNextFile();
}
root.close();
}
} // namespace
#endif
// Callers must hold the SPI lock; recursion prevents taking it here.
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels, size_t maxCount, bool *wasLimited)
{
std::vector<meshtastic_FileInfo> filenames = {};
if (wasLimited)
*wasLimited = false;
#ifdef FSCom
#if defined(__cpp_exceptions) || defined(__EXCEPTIONS)
size_t reservedCount = maxCount;
while (reservedCount > 0) {
try {
filenames.reserve(reservedCount);
break;
} catch (const std::bad_alloc &) {
reservedCount /= 2;
} catch (const std::length_error &) {
reservedCount /= 2;
}
}
if (reservedCount == 0) {
if (wasLimited)
*wasLimited = true;
return filenames;
}
if (reservedCount < maxCount) {
if (wasLimited)
*wasLimited = true;
maxCount = reservedCount;
}
#endif
collectFiles(dirname, levels, maxCount, filenames, wasLimited);
#endif
return filenames;
}
@@ -335,4 +409,4 @@ void setupSDCard()
LOG_DEBUG("Total space: %lu MB", (uint32_t)(SD.totalBytes() / (1024 * 1024)));
LOG_DEBUG("Used space: %lu MB", (uint32_t)(SD.usedBytes() / (1024 * 1024)));
#endif
}
}
+2 -2
View File
@@ -52,7 +52,7 @@ void fsInit();
void fsListFiles();
bool copyFile(const char *from, const char *to);
bool renameFile(const char *pathFrom, const char *pathTo);
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels);
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels, size_t maxCount = 64, bool *wasLimited = nullptr);
void listDir(const char *dirname, uint8_t levels, bool del = false);
void rmDir(const char *dirname);
void setupSDCard();
void setupSDCard();
+1
View File
@@ -360,6 +360,7 @@ void MessageStore::clearAllMessages()
resetMessagePool();
#ifdef FSCom
concurrency::LockGuard guard(spiLock);
SafeFile f(filename.c_str(), false);
uint8_t count = 0;
f.write(&count, 1); // write "0 messages"
+5 -3
View File
@@ -832,7 +832,10 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
switch (newState) {
case GPS_ACTIVE:
case GPS_IDLE:
if (oldState == GPS_ACTIVE || oldState == GPS_IDLE) // If hardware already awake, no changes needed
if (oldState == GPS_ACTIVE)
break;
gotTime = false;
if (oldState == GPS_IDLE) // If hardware already awake, no changes needed
break;
if (oldState != GPS_ACTIVE && oldState != GPS_IDLE) // If hardware just waking now, clear buffer
clearBuffer();
@@ -1142,8 +1145,7 @@ int32_t GPS::runOnce()
// if gps_update_interval is <=10s, GPS never goes off, so we treat that differently
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval);
// 1. Got a time for the first time
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
// 1. Got a time for the first time this cycle
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
gotTime = true;
}
+1
View File
@@ -163,6 +163,7 @@ class GPS : private concurrency::OSThread
uint32_t lastChecksumFailCount = 0;
uint8_t currentStep = 0;
int32_t currentDelay = 2000;
bool gotTime = false;
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
+2 -2
View File
@@ -262,8 +262,8 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
} else if (q == RTCQualityGPS) {
shouldSet = true;
LOG_DEBUG("Reapply GPS time: %ld secs", printableEpoch);
} else if (q == RTCQualityNTP && !Throttle::isWithinTimespanMs(lastSetMsec, (12 * 60 * 60 * 1000UL))) {
// Every 12 hrs we will slam in a new NTP or Phone GPS / NTP time, to correct for local RTC clock drift
} else if (q == RTCQualityNTP && !Throttle::isWithinTimespanMs(lastSetMsec, (30 * 60 * 1000UL))) {
// Every 30 minutes we will slam in a new NTP or Phone GPS / NTP time, to correct for local RTC clock drift
shouldSet = true;
LOG_DEBUG("Reapply external time to correct clock drift %ld secs", printableEpoch);
} else {
+4
View File
@@ -9,6 +9,7 @@
#include "FSCommon.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "MessageStore.h"
#include "NodeDB.h"
#include "PacketHistory.h"
#include "PowerFSM.h"
@@ -524,6 +525,9 @@ bool NodeDB::factoryReset(bool eraseBleBonds)
if (transmitHistory) {
transmitHistory->clear();
}
#if HAS_SCREEN
messageStore.clearAllMessages();
#endif
// second, install default state (this will deal with the duplicate mac address issue)
installDefaultNodeDatabase();
installDefaultDeviceState();
+30 -7
View File
@@ -36,6 +36,17 @@
// Flag to indicate a heartbeat was received and we should send queue status
bool heartbeatReceived = false;
namespace
{
constexpr uint8_t FILES_MANIFEST_LEVELS = 3;
constexpr size_t FILES_MANIFEST_MAX_COUNT = 64;
void releaseFilesManifest(std::vector<meshtastic_FileInfo> &filesManifest)
{
std::vector<meshtastic_FileInfo>().swap(filesManifest);
}
} // namespace
PhoneAPI::PhoneAPI()
{
lastContactMsec = millis();
@@ -70,10 +81,23 @@ void PhoneAPI::handleStartConfig()
state = STATE_SEND_MY_INFO;
}
pauseBluetoothLogging = true;
spiLock->lock();
filesManifest = getFiles("/", 10);
spiLock->unlock();
LOG_DEBUG("Got %d files in manifest", filesManifest.size());
// Manifest is never read on the node-info-only path (STATE_SEND_FILEMANIFEST
// short-circuits to sendConfigComplete), so skip the SPI lock + FS walk.
if (config_nonce != SPECIAL_NONCE_ONLY_NODES) {
bool filesManifestLimited = false;
{
concurrency::LockGuard guard(spiLock);
filesManifest = getFiles("/", FILES_MANIFEST_LEVELS, FILES_MANIFEST_MAX_COUNT, &filesManifestLimited);
}
if (filesManifestLimited) {
LOG_WARN("Got %zu files in manifest (limited to %zu entries/depth %u)", filesManifest.size(),
FILES_MANIFEST_MAX_COUNT, static_cast<unsigned>(FILES_MANIFEST_LEVELS));
} else {
LOG_DEBUG("Got %zu files in manifest", filesManifest.size());
}
} else {
releaseFilesManifest(filesManifest);
}
LOG_INFO("Start API client config millis=%u", millis());
// Protect against concurrent BLE callbacks: they run in NimBLE's FreeRTOS task and also touch nodeInfoQueue.
@@ -122,8 +146,7 @@ void PhoneAPI::close()
nodeInfoQueue.clear();
}
packetForPhone = NULL;
filesManifest.clear();
filesManifest.shrink_to_fit();
releaseFilesManifest(filesManifest);
lastPortNumToRadio.clear();
fromRadioNum = 0;
config_nonce = 0;
@@ -532,7 +555,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
if (config_state == filesManifest.size() ||
config_nonce == SPECIAL_NONCE_ONLY_NODES) { // also handles an empty filesManifest
config_state = 0;
filesManifest.clear();
releaseFilesManifest(filesManifest);
// Skip to complete packet
sendConfigComplete();
} else {
+2
View File
@@ -32,6 +32,8 @@
#include "STM32WLE5JCInterface.h"
#endif
Observable<uint32_t> RadioInterface::loraRxPacketObservable;
#define RDEF(name, freq_start, freq_end, duty_cycle, spacing, power_limit, audio_permitted, frequency_switching, wide_lora) \
{ \
meshtastic_Config_LoRaConfig_RegionCode_##name, freq_start, freq_end, duty_cycle, spacing, power_limit, audio_permitted, \
+3
View File
@@ -123,6 +123,9 @@ class RadioInterface
virtual ~RadioInterface() {}
/// Fires once per valid received LoRa packet (arg = sender NodeNum). Used e.g. to flash LED_LORA.
static Observable<uint32_t> loraRxPacketObservable;
/**
* Coerce LoRa config fields (bandwidth/spread_factor) derived from presets.
* This is used during early bootstrapping so UIs that display these fields directly remain consistent.
+10
View File
@@ -509,6 +509,9 @@ void RadioLibInterface::completeSending()
// that can take a long time
auto p = sendingPacket;
sendingPacket = NULL;
#ifdef LED_LORA
digitalWrite(LED_LORA, LED_STATE_OFF);
#endif
if (p) {
// Packet has been sent, count it toward our TX airtime utilization.
@@ -611,6 +614,10 @@ void RadioLibInterface::handleReceiveInterrupt()
printPacket("Lora RX", mp);
#ifdef LED_LORA
loraRxPacketObservable.notifyObservers(mp->from);
#endif
airTime->logAirtime(RX_LOG, rxMsec);
deliverToReceiver(mp);
@@ -686,6 +693,9 @@ bool RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
enableInterrupt(isrTxLevel0);
lastTxStart = millis();
printPacket("Started Tx", txp);
#ifdef LED_LORA
digitalWrite(LED_LORA, LED_STATE_ON);
#endif
}
return res == RADIOLIB_ERR_NONE;
+10 -1
View File
@@ -30,7 +30,7 @@ PB_BIND(meshtastic_AircraftTrack, meshtastic_AircraftTrack, AUTO)
PB_BIND(meshtastic_CotGeoPoint, meshtastic_CotGeoPoint, AUTO)
PB_BIND(meshtastic_DrawnShape, meshtastic_DrawnShape, 2)
PB_BIND(meshtastic_DrawnShape, meshtastic_DrawnShape, AUTO)
PB_BIND(meshtastic_Marker, meshtastic_Marker, AUTO)
@@ -63,6 +63,15 @@ PB_BIND(meshtastic_TAKEnvironment, meshtastic_TAKEnvironment, AUTO)
PB_BIND(meshtastic_SensorFov, meshtastic_SensorFov, AUTO)
PB_BIND(meshtastic_TakTalkMessage, meshtastic_TakTalkMessage, AUTO)
PB_BIND(meshtastic_TakTalkRoomData, meshtastic_TakTalkRoomData, AUTO)
PB_BIND(meshtastic_Marti, meshtastic_Marti, AUTO)
PB_BIND(meshtastic_TAKPacketV2, meshtastic_TAKPacketV2, 2)
+214 -31
View File
@@ -325,7 +325,15 @@ typedef enum _meshtastic_CotType {
meshtastic_CotType_CotType_b_a_o_c = 123,
/* t-s: Task / engage request. Structured payload carried via the new
TaskRequest typed variant. */
meshtastic_CotType_CotType_t_s = 124
meshtastic_CotType_CotType_t_s = 124,
/* m-t-t: TAKTALK voice/text chat message. Payload carried via the
TakTalkMessage typed variant (text, chatroom_id, lang, from_voice). */
meshtastic_CotType_CotType_m_t_t = 125,
/* y-: TAKTALK room/membership broadcast. Payload carried via the
TakTalkRoomData typed variant (sender_callsign, room_id, room_name,
participants). The CoT type literally has a trailing dash and no
second atom — not a typo. */
meshtastic_CotType_CotType_y = 126
} meshtastic_CotType;
/* Geopoint and altitude source */
@@ -553,6 +561,18 @@ typedef struct _meshtastic_GeoChat {
/* Receipt kind discriminator. See ReceiptType doc. Default ReceiptType_None
means this is a regular chat message, not a receipt. */
meshtastic_GeoChat_ReceiptType receipt_type;
/* BCP-47-ish language tag or human-readable name (e.g. "en", "English")
that the originator's TAKTALK plugin recorded for the message. */
pb_callback_t lang;
/* TAKTALK chatroom UUID (e.g. "30b2755c-c547-44ef-a0cc-cdbd8a15616f") that
the receiver's TAKTALK plugin uses to thread the message under the
right room. Resolved to a friendly name via TakTalkRoomData broadcasts. */
pb_callback_t room_id;
/* TAKTALK voice profile pointer. Often empty in practice (the empty
marker `<voice_profile_id/>` still signals TAKTALK origination), so
receivers should treat empty-but-present as the equivalent of the
marker rather than a missing field. */
pb_callback_t voice_profile_id;
} meshtastic_GeoChat;
/* ATAK Group
@@ -715,12 +735,7 @@ typedef struct _meshtastic_DrawnShape {
uint32_t fill_argb;
/* Whether labels are rendered on this shape. */
bool labels_on;
/* Vertex list for polyline/polygon/rectangle shapes. Capped at 32 by
the nanopb pool; senders MUST truncate longer inputs and set
`truncated = true`. */
pb_size_t vertices_count;
meshtastic_CotGeoPoint vertices[32];
/* True if the sender truncated `vertices` to fit the pool. */
/* True if the sender truncated the vertex columns to fit the pool. */
bool truncated; /* --- Bullseye-only fields. All ignored unless kind == Kind_Bullseye. --- */
/* Bullseye distance in meters * 10 (e.g. 3285 = 328.5 m). 0 = unset. */
uint32_t bullseye_distance_dm;
@@ -734,6 +749,8 @@ typedef struct _meshtastic_DrawnShape {
uint8_t bullseye_flags;
/* Bullseye reference UID (anchor marker). Empty = anchor is self. */
char bullseye_uid_ref[48];
pb_callback_t vertex_lat_deltas;
pb_callback_t vertex_lon_deltas;
} meshtastic_DrawnShape;
/* Fixed point of interest: spot marker, waypoint, checkpoint, 2525 symbol,
@@ -1068,6 +1085,87 @@ typedef struct _meshtastic_SensorFov {
pb_callback_t model;
} meshtastic_SensorFov;
/* TAKTALK chat message payload (CoT type m-t-t).
TAKTALK is an ATAK plugin for voice + text team messaging. The voice
audio stream goes over UDP/RTP and is NOT carried by the mesh — only
the text envelope (this message) is. `from_voice` marks messages sent
via push-to-talk speech-to-text so receivers can render a mic icon
next to the text.
Wire shape inside <event type="m-t-t">/<detail>:
<callsign>...</callsign> - mapped to TAKPacketV2.callsign
<lang>English</lang> - lang
<text>...</text> - text
<chatroom-id>1</chatroom-id> - chatroom_id
<voice/> - presence sets from_voice = true */
typedef struct _meshtastic_TakTalkMessage {
/* The text body of the TAKTALK message (speech-to-text transcript when
from_voice = true, typed message otherwise). */
pb_callback_t text;
/* TAKTALK chatroom identifier. May be a short id like "1" for the
default room or a UUID like "30b2755c-c547-44ef-a0cc-cdbd8a15616f"
for custom rooms (resolved by TakTalkRoomData broadcasts).
Empty = broadcast room. */
pb_callback_t chatroom_id;
/* BCP-47-ish language tag or human-readable name (e.g. "en", "English").
Empty = unspecified. */
pb_callback_t lang;
/* True when the source CoT carried a <voice/> marker, i.e. the message
originated as push-to-talk speech-to-text. Lets receivers show a mic
icon. Proto3 only encodes when true so empty payload cost is 0 bytes. */
bool from_voice;
} meshtastic_TakTalkMessage;
/* TAKTALK room/membership broadcast (CoT type y-).
Announces a TAKTALK chatroom's friendly name and roster so peers can
resolve room UUIDs (used in TakTalkMessage.chatroom_id and
GeoChat.room_id) to a display name and participant list. Not a chat
message itself — these events are emitted by TAKTALK when rooms are
created or memberships change. */
typedef struct _meshtastic_TakTalkRoomData {
/* Callsign of the device broadcasting the room state (typically the
room owner / latest writer).
DEPRECATED in v0.3.2: always equals TAKPacketV2.callsign, so the wire
byte was redundant. Builders stop emitting this field in v0.3.2;
parsers still read it for one release so v0.3.1-encoded packets decode
cleanly. To be removed entirely in v0.4.x. */
pb_callback_t sender_callsign;
/* Room UUID, matches TakTalkMessage.chatroom_id / GeoChat.room_id on
messages routed into this room. */
pb_callback_t room_id;
/* Friendly display name for the room (e.g. "test", "Alpha Team"). */
pb_callback_t room_name;
/* Member callsigns. Wire-encoded as repeated strings; the underlying
CoT carries them as a single <chatroom-participants>A,B,C</> element
which parsers split / builders join on ','. */
pb_callback_t participants;
} meshtastic_TakTalkRoomData;
/* ATAK directed-routing recipient list (CoT <marti><dest callsign='X'/>…</marti>).
Present when an event is addressed to specific TAK users rather than the
broadcast group. TAKTALK gates voice TTS on this element matching the
receiver's callsign; directed b-t-f chats use it for the same purpose. A
missing <marti> means "broadcast to all peers", which is the default for
PLI, alerts, drawings, and most situational-awareness events.
Carried as repeated strings (not indexes into a per-packet table) because
the typical event has 1-2 destinations and table overhead would erase the
savings. Receivers that need the original XML element rebuild it from
dest_callsign on emit. */
typedef struct _meshtastic_Marti {
/* Recipient callsigns. Order is preserved end-to-end so receivers can show
primary-vs-cc distinction the same way ATAK does.
If dest_callsign is [TAKPacketV2.callsign] (self-addressed, unusual but
legal — e.g. ATAK echoing back to its own room), the builder still emits
the element so loopback shapes round-trip cleanly. */
pb_callback_t dest_callsign;
} meshtastic_Marti;
typedef PB_BYTES_ARRAY_T(220) meshtastic_TAKPacketV2_raw_detail_t;
/* ATAK v2 packet with expanded CoT field support and zstd dictionary compression.
Sent on ATAK_PLUGIN_V2 port. The wire payload is:
@@ -1089,7 +1187,14 @@ typedef struct _meshtastic_TAKPacketV2 {
int32_t latitude_i;
/* Longitude, multiply by 1e-7 to get degrees in floating point */
int32_t longitude_i;
/* Altitude in meters (HAE) */
/* Altitude in meters (HAE). ATAK's "no altitude" sentinel is hae=9999999.0.
NOTE: an earlier v0.4.0 attempt made this `optional` to omit the 9999999
sentinel from the wire, but measurement showed it was net-negative: the
zstd dictionary already compresses the literal 9999999 to ~nothing, while
proto3 `optional` forces a genuine 0 m HAE (common on routes/drawings that
carry hae="0.0" or omit hae → parsed as 0) to encode explicitly (+2 bytes),
which REGRESSED the worst-case route fixture. Kept as a plain field. */
int32_t altitude;
/* Speed in cm/s */
uint32_t speed;
@@ -1135,10 +1240,18 @@ typedef struct _meshtastic_TAKPacketV2 {
/* Sensor field-of-view cone (camera, FLIR, laser, etc.). From <sensor>. */
bool has_sensor_fov;
meshtastic_SensorFov sensor_fov;
/* Directed-routing recipient list (CoT <marti><dest callsign='X'/>…</marti>).
Empty / unset = broadcast to all peers (the default for situational-awareness
events). Populated for TAKTALK m-t-t, directed b-t-f DMs, and any other CoT
shape that ATAK addresses to specific recipients. TAKTALK gates voice TTS
playback on this element matching the receiver's callsign, so dropping it
silently breaks voice messaging end-to-end.
See Marti. */
bool has_marti;
meshtastic_Marti marti;
pb_size_t which_payload_variant;
union {
/* Position report (true = PLI, no extra fields beyond the common ones above) */
bool pli;
/* ATAK GeoChat message */
meshtastic_GeoChat chat;
/* Aircraft track data (ADS-B, military air) */
@@ -1163,6 +1276,14 @@ typedef struct _meshtastic_TAKPacketV2 {
meshtastic_EmergencyAlert emergency;
/* Task / engage request. See TaskRequest. */
meshtastic_TaskRequest task;
/* TAKTALK chat message (CoT type m-t-t). See TakTalkMessage.
Voice audio itself rides UDP/RTP outside the mesh; this carries the
text envelope plus a from_voice marker for receiver UX. */
meshtastic_TakTalkMessage taktalk;
/* TAKTALK room/membership broadcast (CoT type y-). See TakTalkRoomData.
Resolves room UUIDs (used in TakTalkMessage.chatroom_id and
GeoChat.room_id) to display name + roster on receivers. */
meshtastic_TakTalkRoomData taktalk_room;
} payload_variant;
} meshtastic_TAKPacketV2;
@@ -1185,8 +1306,8 @@ extern "C" {
#define _meshtastic_CotHow_ARRAYSIZE ((meshtastic_CotHow)(meshtastic_CotHow_CotHow_m_s+1))
#define _meshtastic_CotType_MIN meshtastic_CotType_CotType_Other
#define _meshtastic_CotType_MAX meshtastic_CotType_CotType_t_s
#define _meshtastic_CotType_ARRAYSIZE ((meshtastic_CotType)(meshtastic_CotType_CotType_t_s+1))
#define _meshtastic_CotType_MAX meshtastic_CotType_CotType_y
#define _meshtastic_CotType_ARRAYSIZE ((meshtastic_CotType)(meshtastic_CotType_CotType_y+1))
#define _meshtastic_GeoPointSource_MIN meshtastic_GeoPointSource_GeoPointSource_Unspecified
#define _meshtastic_GeoPointSource_MAX meshtastic_GeoPointSource_GeoPointSource_NETWORK
@@ -1282,6 +1403,9 @@ extern "C" {
#define meshtastic_SensorFov_type_ENUMTYPE meshtastic_SensorFov_SensorType
#define meshtastic_TAKPacketV2_cot_type_id_ENUMTYPE meshtastic_CotType
#define meshtastic_TAKPacketV2_how_ENUMTYPE meshtastic_CotHow
#define meshtastic_TAKPacketV2_team_ENUMTYPE meshtastic_Team
@@ -1292,14 +1416,14 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_TAKPacket_init_default {0, false, meshtastic_Contact_init_default, false, meshtastic_Group_init_default, false, meshtastic_Status_init_default, 0, {meshtastic_PLI_init_default}}
#define meshtastic_GeoChat_init_default {"", false, "", false, "", "", _meshtastic_GeoChat_ReceiptType_MIN}
#define meshtastic_GeoChat_init_default {"", false, "", false, "", "", _meshtastic_GeoChat_ReceiptType_MIN, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
#define meshtastic_Group_init_default {_meshtastic_MemberRole_MIN, _meshtastic_Team_MIN}
#define meshtastic_Status_init_default {0}
#define meshtastic_Contact_init_default {"", ""}
#define meshtastic_PLI_init_default {0, 0, 0, 0, 0}
#define meshtastic_AircraftTrack_init_default {"", "", "", "", 0, "", 0, 0, ""}
#define meshtastic_CotGeoPoint_init_default {0, 0}
#define meshtastic_DrawnShape_init_default {_meshtastic_DrawnShape_Kind_MIN, _meshtastic_DrawnShape_StyleMode_MIN, 0, 0, 0, _meshtastic_Team_MIN, 0, 0, _meshtastic_Team_MIN, 0, 0, 0, {meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default, meshtastic_CotGeoPoint_init_default}, 0, 0, 0, 0, ""}
#define meshtastic_DrawnShape_init_default {_meshtastic_DrawnShape_Kind_MIN, _meshtastic_DrawnShape_StyleMode_MIN, 0, 0, 0, _meshtastic_Team_MIN, 0, 0, _meshtastic_Team_MIN, 0, 0, 0, 0, 0, 0, "", {{NULL}, NULL}, {{NULL}, NULL}}
#define meshtastic_Marker_init_default {_meshtastic_Marker_Kind_MIN, _meshtastic_Team_MIN, 0, 0, "", "", "", ""}
#define meshtastic_RangeAndBearing_init_default {false, meshtastic_CotGeoPoint_init_default, "", 0, 0, _meshtastic_Team_MIN, 0, 0}
#define meshtastic_Route_init_default {_meshtastic_Route_Method_MIN, _meshtastic_Route_Direction_MIN, "", 0, 0, {meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default, meshtastic_Route_Link_init_default}, 0}
@@ -1310,16 +1434,19 @@ extern "C" {
#define meshtastic_TaskRequest_init_default {"", "", "", _meshtastic_TaskRequest_Priority_MIN, _meshtastic_TaskRequest_Status_MIN, ""}
#define meshtastic_TAKEnvironment_init_default {0, 0, 0}
#define meshtastic_SensorFov_init_default {_meshtastic_SensorFov_SensorType_MIN, 0, false, 0, 0, 0, 0, 0, {{NULL}, NULL}}
#define meshtastic_TAKPacketV2_init_default {_meshtastic_CotType_MIN, _meshtastic_CotHow_MIN, "", _meshtastic_Team_MIN, _meshtastic_MemberRole_MIN, 0, 0, 0, 0, 0, 0, _meshtastic_GeoPointSource_MIN, _meshtastic_GeoPointSource_MIN, "", "", 0, "", "", "", "", "", "", "", {{NULL}, NULL}, false, meshtastic_TAKEnvironment_init_default, false, meshtastic_SensorFov_init_default, 0, {0}}
#define meshtastic_TakTalkMessage_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0}
#define meshtastic_TakTalkRoomData_init_default {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
#define meshtastic_Marti_init_default {{{NULL}, NULL}}
#define meshtastic_TAKPacketV2_init_default {_meshtastic_CotType_MIN, _meshtastic_CotHow_MIN, "", _meshtastic_Team_MIN, _meshtastic_MemberRole_MIN, 0, 0, 0, 0, 0, 0, _meshtastic_GeoPointSource_MIN, _meshtastic_GeoPointSource_MIN, "", "", 0, "", "", "", "", "", "", "", {{NULL}, NULL}, false, meshtastic_TAKEnvironment_init_default, false, meshtastic_SensorFov_init_default, false, meshtastic_Marti_init_default, 0, {meshtastic_GeoChat_init_default}}
#define meshtastic_TAKPacket_init_zero {0, false, meshtastic_Contact_init_zero, false, meshtastic_Group_init_zero, false, meshtastic_Status_init_zero, 0, {meshtastic_PLI_init_zero}}
#define meshtastic_GeoChat_init_zero {"", false, "", false, "", "", _meshtastic_GeoChat_ReceiptType_MIN}
#define meshtastic_GeoChat_init_zero {"", false, "", false, "", "", _meshtastic_GeoChat_ReceiptType_MIN, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
#define meshtastic_Group_init_zero {_meshtastic_MemberRole_MIN, _meshtastic_Team_MIN}
#define meshtastic_Status_init_zero {0}
#define meshtastic_Contact_init_zero {"", ""}
#define meshtastic_PLI_init_zero {0, 0, 0, 0, 0}
#define meshtastic_AircraftTrack_init_zero {"", "", "", "", 0, "", 0, 0, ""}
#define meshtastic_CotGeoPoint_init_zero {0, 0}
#define meshtastic_DrawnShape_init_zero {_meshtastic_DrawnShape_Kind_MIN, _meshtastic_DrawnShape_StyleMode_MIN, 0, 0, 0, _meshtastic_Team_MIN, 0, 0, _meshtastic_Team_MIN, 0, 0, 0, {meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero, meshtastic_CotGeoPoint_init_zero}, 0, 0, 0, 0, ""}
#define meshtastic_DrawnShape_init_zero {_meshtastic_DrawnShape_Kind_MIN, _meshtastic_DrawnShape_StyleMode_MIN, 0, 0, 0, _meshtastic_Team_MIN, 0, 0, _meshtastic_Team_MIN, 0, 0, 0, 0, 0, 0, "", {{NULL}, NULL}, {{NULL}, NULL}}
#define meshtastic_Marker_init_zero {_meshtastic_Marker_Kind_MIN, _meshtastic_Team_MIN, 0, 0, "", "", "", ""}
#define meshtastic_RangeAndBearing_init_zero {false, meshtastic_CotGeoPoint_init_zero, "", 0, 0, _meshtastic_Team_MIN, 0, 0}
#define meshtastic_Route_init_zero {_meshtastic_Route_Method_MIN, _meshtastic_Route_Direction_MIN, "", 0, 0, {meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero, meshtastic_Route_Link_init_zero}, 0}
@@ -1330,7 +1457,10 @@ extern "C" {
#define meshtastic_TaskRequest_init_zero {"", "", "", _meshtastic_TaskRequest_Priority_MIN, _meshtastic_TaskRequest_Status_MIN, ""}
#define meshtastic_TAKEnvironment_init_zero {0, 0, 0}
#define meshtastic_SensorFov_init_zero {_meshtastic_SensorFov_SensorType_MIN, 0, false, 0, 0, 0, 0, 0, {{NULL}, NULL}}
#define meshtastic_TAKPacketV2_init_zero {_meshtastic_CotType_MIN, _meshtastic_CotHow_MIN, "", _meshtastic_Team_MIN, _meshtastic_MemberRole_MIN, 0, 0, 0, 0, 0, 0, _meshtastic_GeoPointSource_MIN, _meshtastic_GeoPointSource_MIN, "", "", 0, "", "", "", "", "", "", "", {{NULL}, NULL}, false, meshtastic_TAKEnvironment_init_zero, false, meshtastic_SensorFov_init_zero, 0, {0}}
#define meshtastic_TakTalkMessage_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, 0}
#define meshtastic_TakTalkRoomData_init_zero {{{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}, {{NULL}, NULL}}
#define meshtastic_Marti_init_zero {{{NULL}, NULL}}
#define meshtastic_TAKPacketV2_init_zero {_meshtastic_CotType_MIN, _meshtastic_CotHow_MIN, "", _meshtastic_Team_MIN, _meshtastic_MemberRole_MIN, 0, 0, 0, 0, 0, 0, _meshtastic_GeoPointSource_MIN, _meshtastic_GeoPointSource_MIN, "", "", 0, "", "", "", "", "", "", "", {{NULL}, NULL}, false, meshtastic_TAKEnvironment_init_zero, false, meshtastic_SensorFov_init_zero, false, meshtastic_Marti_init_zero, 0, {meshtastic_GeoChat_init_zero}}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_GeoChat_message_tag 1
@@ -1338,6 +1468,9 @@ extern "C" {
#define meshtastic_GeoChat_to_callsign_tag 3
#define meshtastic_GeoChat_receipt_for_uid_tag 4
#define meshtastic_GeoChat_receipt_type_tag 5
#define meshtastic_GeoChat_lang_tag 6
#define meshtastic_GeoChat_room_id_tag 7
#define meshtastic_GeoChat_voice_profile_id_tag 8
#define meshtastic_Group_role_tag 1
#define meshtastic_Group_team_tag 2
#define meshtastic_Status_battery_tag 1
@@ -1377,12 +1510,13 @@ extern "C" {
#define meshtastic_DrawnShape_fill_color_tag 9
#define meshtastic_DrawnShape_fill_argb_tag 10
#define meshtastic_DrawnShape_labels_on_tag 11
#define meshtastic_DrawnShape_vertices_tag 12
#define meshtastic_DrawnShape_truncated_tag 13
#define meshtastic_DrawnShape_bullseye_distance_dm_tag 14
#define meshtastic_DrawnShape_bullseye_bearing_ref_tag 15
#define meshtastic_DrawnShape_bullseye_flags_tag 16
#define meshtastic_DrawnShape_bullseye_uid_ref_tag 17
#define meshtastic_DrawnShape_vertex_lat_deltas_tag 18
#define meshtastic_DrawnShape_vertex_lon_deltas_tag 19
#define meshtastic_Marker_kind_tag 1
#define meshtastic_Marker_color_tag 2
#define meshtastic_Marker_color_argb_tag 3
@@ -1467,6 +1601,15 @@ extern "C" {
#define meshtastic_SensorFov_elevation_deg_tag 6
#define meshtastic_SensorFov_roll_deg_tag 7
#define meshtastic_SensorFov_model_tag 8
#define meshtastic_TakTalkMessage_text_tag 1
#define meshtastic_TakTalkMessage_chatroom_id_tag 2
#define meshtastic_TakTalkMessage_lang_tag 3
#define meshtastic_TakTalkMessage_from_voice_tag 4
#define meshtastic_TakTalkRoomData_sender_callsign_tag 1
#define meshtastic_TakTalkRoomData_room_id_tag 2
#define meshtastic_TakTalkRoomData_room_name_tag 3
#define meshtastic_TakTalkRoomData_participants_tag 4
#define meshtastic_Marti_dest_callsign_tag 1
#define meshtastic_TAKPacketV2_cot_type_id_tag 1
#define meshtastic_TAKPacketV2_how_tag 2
#define meshtastic_TAKPacketV2_callsign_tag 3
@@ -1493,7 +1636,7 @@ extern "C" {
#define meshtastic_TAKPacketV2_remarks_tag 24
#define meshtastic_TAKPacketV2_environment_tag 25
#define meshtastic_TAKPacketV2_sensor_fov_tag 26
#define meshtastic_TAKPacketV2_pli_tag 30
#define meshtastic_TAKPacketV2_marti_tag 29
#define meshtastic_TAKPacketV2_chat_tag 31
#define meshtastic_TAKPacketV2_aircraft_tag 32
#define meshtastic_TAKPacketV2_raw_detail_tag 33
@@ -1504,6 +1647,8 @@ extern "C" {
#define meshtastic_TAKPacketV2_casevac_tag 38
#define meshtastic_TAKPacketV2_emergency_tag 39
#define meshtastic_TAKPacketV2_task_tag 40
#define meshtastic_TAKPacketV2_taktalk_tag 41
#define meshtastic_TAKPacketV2_taktalk_room_tag 42
/* Struct field encoding specification for nanopb */
#define meshtastic_TAKPacket_FIELDLIST(X, a) \
@@ -1527,8 +1672,11 @@ X(a, STATIC, SINGULAR, STRING, message, 1) \
X(a, STATIC, OPTIONAL, STRING, to, 2) \
X(a, STATIC, OPTIONAL, STRING, to_callsign, 3) \
X(a, STATIC, SINGULAR, STRING, receipt_for_uid, 4) \
X(a, STATIC, SINGULAR, UENUM, receipt_type, 5)
#define meshtastic_GeoChat_CALLBACK NULL
X(a, STATIC, SINGULAR, UENUM, receipt_type, 5) \
X(a, CALLBACK, OPTIONAL, STRING, lang, 6) \
X(a, CALLBACK, OPTIONAL, STRING, room_id, 7) \
X(a, CALLBACK, OPTIONAL, STRING, voice_profile_id, 8)
#define meshtastic_GeoChat_CALLBACK pb_default_field_callback
#define meshtastic_GeoChat_DEFAULT NULL
#define meshtastic_Group_FIELDLIST(X, a) \
@@ -1588,15 +1736,15 @@ X(a, STATIC, SINGULAR, UINT32, stroke_weight_x10, 8) \
X(a, STATIC, SINGULAR, UENUM, fill_color, 9) \
X(a, STATIC, SINGULAR, FIXED32, fill_argb, 10) \
X(a, STATIC, SINGULAR, BOOL, labels_on, 11) \
X(a, STATIC, REPEATED, MESSAGE, vertices, 12) \
X(a, STATIC, SINGULAR, BOOL, truncated, 13) \
X(a, STATIC, SINGULAR, UINT32, bullseye_distance_dm, 14) \
X(a, STATIC, SINGULAR, UINT32, bullseye_bearing_ref, 15) \
X(a, STATIC, SINGULAR, UINT32, bullseye_flags, 16) \
X(a, STATIC, SINGULAR, STRING, bullseye_uid_ref, 17)
#define meshtastic_DrawnShape_CALLBACK NULL
X(a, STATIC, SINGULAR, STRING, bullseye_uid_ref, 17) \
X(a, CALLBACK, REPEATED, SINT32, vertex_lat_deltas, 18) \
X(a, CALLBACK, REPEATED, SINT32, vertex_lon_deltas, 19)
#define meshtastic_DrawnShape_CALLBACK pb_default_field_callback
#define meshtastic_DrawnShape_DEFAULT NULL
#define meshtastic_DrawnShape_vertices_MSGTYPE meshtastic_CotGeoPoint
#define meshtastic_Marker_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, kind, 1) \
@@ -1726,6 +1874,27 @@ X(a, CALLBACK, SINGULAR, STRING, model, 8)
#define meshtastic_SensorFov_CALLBACK pb_default_field_callback
#define meshtastic_SensorFov_DEFAULT NULL
#define meshtastic_TakTalkMessage_FIELDLIST(X, a) \
X(a, CALLBACK, SINGULAR, STRING, text, 1) \
X(a, CALLBACK, SINGULAR, STRING, chatroom_id, 2) \
X(a, CALLBACK, SINGULAR, STRING, lang, 3) \
X(a, STATIC, SINGULAR, BOOL, from_voice, 4)
#define meshtastic_TakTalkMessage_CALLBACK pb_default_field_callback
#define meshtastic_TakTalkMessage_DEFAULT NULL
#define meshtastic_TakTalkRoomData_FIELDLIST(X, a) \
X(a, CALLBACK, SINGULAR, STRING, sender_callsign, 1) \
X(a, CALLBACK, SINGULAR, STRING, room_id, 2) \
X(a, CALLBACK, SINGULAR, STRING, room_name, 3) \
X(a, CALLBACK, REPEATED, STRING, participants, 4)
#define meshtastic_TakTalkRoomData_CALLBACK pb_default_field_callback
#define meshtastic_TakTalkRoomData_DEFAULT NULL
#define meshtastic_Marti_FIELDLIST(X, a) \
X(a, CALLBACK, REPEATED, STRING, dest_callsign, 1)
#define meshtastic_Marti_CALLBACK pb_default_field_callback
#define meshtastic_Marti_DEFAULT NULL
#define meshtastic_TAKPacketV2_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, cot_type_id, 1) \
X(a, STATIC, SINGULAR, UENUM, how, 2) \
@@ -1753,7 +1922,7 @@ X(a, STATIC, SINGULAR, STRING, cot_type_str, 23) \
X(a, CALLBACK, SINGULAR, STRING, remarks, 24) \
X(a, STATIC, OPTIONAL, MESSAGE, environment, 25) \
X(a, STATIC, OPTIONAL, MESSAGE, sensor_fov, 26) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,pli,payload_variant.pli), 30) \
X(a, STATIC, OPTIONAL, MESSAGE, marti, 29) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,chat,payload_variant.chat), 31) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,aircraft,payload_variant.aircraft), 32) \
X(a, STATIC, ONEOF, BYTES, (payload_variant,raw_detail,payload_variant.raw_detail), 33) \
@@ -1763,11 +1932,14 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,rab,payload_variant.rab), 3
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,route,payload_variant.route), 37) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,casevac,payload_variant.casevac), 38) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,emergency,payload_variant.emergency), 39) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,task,payload_variant.task), 40)
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,task,payload_variant.task), 40) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,taktalk,payload_variant.taktalk), 41) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,taktalk_room,payload_variant.taktalk_room), 42)
#define meshtastic_TAKPacketV2_CALLBACK pb_default_field_callback
#define meshtastic_TAKPacketV2_DEFAULT NULL
#define meshtastic_TAKPacketV2_environment_MSGTYPE meshtastic_TAKEnvironment
#define meshtastic_TAKPacketV2_sensor_fov_MSGTYPE meshtastic_SensorFov
#define meshtastic_TAKPacketV2_marti_MSGTYPE meshtastic_Marti
#define meshtastic_TAKPacketV2_payload_variant_chat_MSGTYPE meshtastic_GeoChat
#define meshtastic_TAKPacketV2_payload_variant_aircraft_MSGTYPE meshtastic_AircraftTrack
#define meshtastic_TAKPacketV2_payload_variant_shape_MSGTYPE meshtastic_DrawnShape
@@ -1777,6 +1949,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,task,payload_variant.task),
#define meshtastic_TAKPacketV2_payload_variant_casevac_MSGTYPE meshtastic_CasevacReport
#define meshtastic_TAKPacketV2_payload_variant_emergency_MSGTYPE meshtastic_EmergencyAlert
#define meshtastic_TAKPacketV2_payload_variant_task_MSGTYPE meshtastic_TaskRequest
#define meshtastic_TAKPacketV2_payload_variant_taktalk_MSGTYPE meshtastic_TakTalkMessage
#define meshtastic_TAKPacketV2_payload_variant_taktalk_room_MSGTYPE meshtastic_TakTalkRoomData
extern const pb_msgdesc_t meshtastic_TAKPacket_msg;
extern const pb_msgdesc_t meshtastic_GeoChat_msg;
@@ -1797,6 +1971,9 @@ extern const pb_msgdesc_t meshtastic_EmergencyAlert_msg;
extern const pb_msgdesc_t meshtastic_TaskRequest_msg;
extern const pb_msgdesc_t meshtastic_TAKEnvironment_msg;
extern const pb_msgdesc_t meshtastic_SensorFov_msg;
extern const pb_msgdesc_t meshtastic_TakTalkMessage_msg;
extern const pb_msgdesc_t meshtastic_TakTalkRoomData_msg;
extern const pb_msgdesc_t meshtastic_Marti_msg;
extern const pb_msgdesc_t meshtastic_TAKPacketV2_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
@@ -1819,20 +1996,27 @@ extern const pb_msgdesc_t meshtastic_TAKPacketV2_msg;
#define meshtastic_TaskRequest_fields &meshtastic_TaskRequest_msg
#define meshtastic_TAKEnvironment_fields &meshtastic_TAKEnvironment_msg
#define meshtastic_SensorFov_fields &meshtastic_SensorFov_msg
#define meshtastic_TakTalkMessage_fields &meshtastic_TakTalkMessage_msg
#define meshtastic_TakTalkRoomData_fields &meshtastic_TakTalkRoomData_msg
#define meshtastic_Marti_fields &meshtastic_Marti_msg
#define meshtastic_TAKPacketV2_fields &meshtastic_TAKPacketV2_msg
/* Maximum encoded size of messages (where known) */
/* meshtastic_TAKPacket_size depends on runtime parameters */
/* meshtastic_GeoChat_size depends on runtime parameters */
/* meshtastic_DrawnShape_size depends on runtime parameters */
/* meshtastic_CasevacReport_size depends on runtime parameters */
/* meshtastic_ZMistEntry_size depends on runtime parameters */
/* meshtastic_SensorFov_size depends on runtime parameters */
/* meshtastic_TakTalkMessage_size depends on runtime parameters */
/* meshtastic_TakTalkRoomData_size depends on runtime parameters */
/* meshtastic_Marti_size depends on runtime parameters */
/* meshtastic_TAKPacketV2_size depends on runtime parameters */
#define MESHTASTIC_MESHTASTIC_ATAK_PB_H_MAX_SIZE meshtastic_Route_size
#define meshtastic_AircraftTrack_size 134
#define meshtastic_Contact_size 242
#define meshtastic_CotGeoPoint_size 12
#define meshtastic_DrawnShape_size 553
#define meshtastic_EmergencyAlert_size 100
#define meshtastic_GeoChat_size 495
#define meshtastic_Group_size 4
#define meshtastic_Marker_size 191
#define meshtastic_PLI_size 31
@@ -1841,7 +2025,6 @@ extern const pb_msgdesc_t meshtastic_TAKPacketV2_msg;
#define meshtastic_Route_size 1379
#define meshtastic_Status_size 3
#define meshtastic_TAKEnvironment_size 18
#define meshtastic_TAKPacket_size 756
#define meshtastic_TaskRequest_size 132
#ifdef __cplusplus
+7 -5
View File
@@ -290,15 +290,17 @@ typedef enum _meshtastic_Config_LoRaConfig_RegionCode {
meshtastic_Config_LoRaConfig_RegionCode_BR_902 = 26,
/* ITU Region 1 Amateur Radio 2m band (144-146 MHz) */
meshtastic_Config_LoRaConfig_RegionCode_ITU1_2M = 27,
/* ITU Region 2 / 3 Amateur Radio 2m band (144-148 MHz) */
meshtastic_Config_LoRaConfig_RegionCode_ITU23_2M = 28,
/* ITU Region 2 Amateur Radio 2m band (144-148 MHz) */
meshtastic_Config_LoRaConfig_RegionCode_ITU2_2M = 28,
/* EU 866MHz band (Band no. 47b of 2006/771/EC and subsequent amendments) for Non-specific short-range devices (SRD) */
meshtastic_Config_LoRaConfig_RegionCode_EU_866 = 29,
/* EU 874MHz and 917MHz bands (Band no. 1 and 4 of 2022/172/EC and subsequent amendments) for Non-specific short-range devices (SRD) */
meshtastic_Config_LoRaConfig_RegionCode_EU_874 = 30,
meshtastic_Config_LoRaConfig_RegionCode_EU_917 = 31,
/* EU 868MHz band, with narrow presets */
meshtastic_Config_LoRaConfig_RegionCode_EU_N_868 = 32
meshtastic_Config_LoRaConfig_RegionCode_EU_N_868 = 32,
/* ITU Region 3 Amateur Radio 2m band (144-148 MHz) */
meshtastic_Config_LoRaConfig_RegionCode_ITU3_2M = 33
} meshtastic_Config_LoRaConfig_RegionCode;
/* Standard predefined channel settings
@@ -734,8 +736,8 @@ extern "C" {
#define _meshtastic_Config_DisplayConfig_CompassOrientation_ARRAYSIZE ((meshtastic_Config_DisplayConfig_CompassOrientation)(meshtastic_Config_DisplayConfig_CompassOrientation_DEGREES_270_INVERTED+1))
#define _meshtastic_Config_LoRaConfig_RegionCode_MIN meshtastic_Config_LoRaConfig_RegionCode_UNSET
#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_EU_N_868
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_EU_N_868+1))
#define _meshtastic_Config_LoRaConfig_RegionCode_MAX meshtastic_Config_LoRaConfig_RegionCode_ITU3_2M
#define _meshtastic_Config_LoRaConfig_RegionCode_ARRAYSIZE ((meshtastic_Config_LoRaConfig_RegionCode)(meshtastic_Config_LoRaConfig_RegionCode_ITU3_2M+1))
#define _meshtastic_Config_LoRaConfig_ModemPreset_MIN meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST
#define _meshtastic_Config_LoRaConfig_ModemPreset_MAX meshtastic_Config_LoRaConfig_ModemPreset_NARROW_SLOW
+4
View File
@@ -325,6 +325,10 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_T_IMPULSE_PLUS = 135,
/* Lilygo T-Echo Card */
meshtastic_HardwareModel_T_ECHO_CARD = 136,
/* Seeed Tracker L2 */
meshtastic_HardwareModel_SEEED_WIO_TRACKER_L2 = 137,
/* Elecrow CrowPanel Advance P4 models, ESP32-P4 and TFT with SX1262 radio plugin */
meshtastic_HardwareModel_CROWPANEL_P4 = 138,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */
+10 -2
View File
@@ -83,7 +83,11 @@ void CannedMessageModule::LaunchWithDestination(NodeNum newDest, uint8_t newChan
// Do NOT override explicit broadcast replies
// Only reuse lastDest in LaunchRepeatDestination()
dest = newDest;
if (newDest == 0) {
dest = NODENUM_BROADCAST;
} else {
dest = newDest;
}
channel = newChannel;
lastDest = dest;
@@ -123,7 +127,11 @@ void CannedMessageModule::LaunchFreetextWithDestination(NodeNum newDest, uint8_t
// Do NOT override explicit broadcast replies
// Only reuse lastDest in LaunchRepeatDestination()
dest = newDest;
if (newDest == 0) {
dest = NODENUM_BROADCAST;
} else {
dest = newDest;
}
channel = newChannel;
lastDest = dest;
+38
View File
@@ -1,6 +1,7 @@
#include "StatusLEDModule.h"
#include "MeshService.h"
#include "configuration.h"
#include "mesh/RadioInterface.h"
#include <Arduino.h>
/*
@@ -17,6 +18,9 @@ StatusLEDModule::StatusLEDModule() : concurrency::OSThread("StatusLEDModule")
if (inputBroker)
inputObserver.observe(inputBroker);
#endif
#ifdef LED_LORA
loraRxObserver.observe(&RadioInterface::loraRxPacketObservable);
#endif
#ifdef NEOPIXEL_STATUS_POWER_PIN
powerPixel.begin();
powerPixel.clear();
@@ -90,6 +94,18 @@ int StatusLEDModule::handleInputEvent(const InputEvent *event)
return 0;
}
#endif
#ifdef LED_LORA
int StatusLEDModule::handleLoRaRx(uint32_t)
{
// Briefly flash LED_LORA on each received packet. Turn it on now (we share the main thread with
// the radio's receive handler, so this is safe) and wake runOnce() at flash end to turn it off.
digitalWrite(LED_LORA, LED_STATE_ON);
LORA_LED_state = LED_STATE_ON;
LORA_LED_starttime = millis();
setIntervalFromNow(LORA_RX_LED_FLASH_MS);
return 0;
}
#endif
int32_t StatusLEDModule::runOnce()
{
@@ -115,6 +131,12 @@ int32_t StatusLEDModule::runOnce()
CHARGE_LED_state = LED_STATE_OFF;
}
}
} else {
#if defined(LED_HEARTBEAT)
// If we are using the heartbeat, as in the Thinknode M4, we need to explicitly turn off the charge LED
// This probably implies that in the future we need to stop re-using this bool for multiple purposes.
CHARGE_LED_state = LED_STATE_OFF;
#endif
}
// If we want a LED to be dedicated to the simple hearbeat, we can use that instead of the charge LED
#if defined(LED_HEARTBEAT)
@@ -142,6 +164,7 @@ int32_t StatusLEDModule::runOnce()
}
}
#endif
#ifdef LED_PAIRING
if (!config.bluetooth.enabled || PAIRING_LED_starttime + 30 * 1000 < millis() || doing_fast_blink) {
PAIRING_LED_state = LED_STATE_OFF;
} else if (ble_state == unpaired) {
@@ -156,6 +179,7 @@ int32_t StatusLEDModule::runOnce()
} else {
PAIRING_LED_state = LED_STATE_ON;
}
#endif
// Override if disabled in config
if (config.device.led_heartbeat_disabled) {
@@ -227,6 +251,20 @@ int32_t StatusLEDModule::runOnce()
digitalWrite(Battery_LED_4, chargeIndicatorLED4);
#endif
#ifdef LED_LORA
// End the LoRa-RX flash once its duration has elapsed; otherwise make sure we come back
// exactly at flash end (only ever clamp my_interval down, so other LED timing is preserved).
if (LORA_LED_state == LED_STATE_ON) {
uint32_t elapsed = millis() - LORA_LED_starttime;
if (elapsed >= LORA_RX_LED_FLASH_MS) {
digitalWrite(LED_LORA, LED_STATE_OFF);
LORA_LED_state = LED_STATE_OFF;
} else if ((uint32_t)my_interval > LORA_RX_LED_FLASH_MS - elapsed) {
my_interval = LORA_RX_LED_FLASH_MS - elapsed;
}
}
#endif
return (my_interval);
}
+12
View File
@@ -43,6 +43,9 @@ class StatusLEDModule : private concurrency::OSThread
#if !MESHTASTIC_EXCLUDE_INPUTBROKER
int handleInputEvent(const InputEvent *arg);
#endif
#ifdef LED_LORA
int handleLoRaRx(uint32_t sender);
#endif
void setPowerLED(bool);
@@ -65,6 +68,10 @@ class StatusLEDModule : private concurrency::OSThread
CallbackObserver<StatusLEDModule, const InputEvent *> inputObserver =
CallbackObserver<StatusLEDModule, const InputEvent *>(this, &StatusLEDModule::handleInputEvent);
#endif
#ifdef LED_LORA
CallbackObserver<StatusLEDModule, uint32_t> loraRxObserver =
CallbackObserver<StatusLEDModule, uint32_t>(this, &StatusLEDModule::handleLoRaRx);
#endif
private:
bool CHARGE_LED_state = LED_STATE_OFF;
@@ -77,6 +84,11 @@ class StatusLEDModule : private concurrency::OSThread
uint32_t lastUserbuttonTime = 0;
uint32_t POWER_LED_starttime = 0;
bool doing_fast_blink = false;
#ifdef LED_LORA
static constexpr uint32_t LORA_RX_LED_FLASH_MS = 100;
bool LORA_LED_state = LED_STATE_OFF;
uint32_t LORA_LED_starttime = 0;
#endif
enum PowerState { discharging, charging, charged, critical };
+5 -2
View File
@@ -15,6 +15,7 @@
#endif
#include "esp_mac.h"
#include "freertosinc.h"
#include "meshUtils.h"
#include "sleep.h"
#include "soc/rtc.h"
@@ -261,6 +262,8 @@ void cpuDeepSleep(uint32_t msecToWake)
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery)
// User shutdown (DELAY_FOREVER / portMAX_DELAY): no RTC timer — align with nRF52 system_off semantics.
if (msecToWake != portMAX_DELAY)
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start();
}
@@ -18,7 +18,9 @@
#define BATTERY_PIN 8
#define ADC_CHANNEL ADC1_GPIO8_CHANNEL
#define ADC_MULTIPLIER 2.0 // 2.0 + 10% for correction of display undervoltage.
#define ADC_MULTIPLIER 2.0
#define OCV_ARRAY 4100, 4050, 3990, 3890, 3800, 3720, 3630, 3530, 3420, 3300, 3100
#define PIN_BUZZER 9
@@ -9,4 +9,6 @@ void initVariant()
{
pinMode(LED_PAIRING, OUTPUT);
digitalWrite(LED_PAIRING, !LED_STATE_ON); // Turn off the LED to start
pinMode(LED_LORA, OUTPUT);
digitalWrite(LED_LORA, !LED_STATE_ON); // Turn off the LED to start
}
@@ -37,6 +37,9 @@ extends = env:picomputer-s3
build_flags =
${env:picomputer-s3.build_flags}
-D MESHTASTIC_EXCLUDE_WEBSERVER=1
; device-ui's I2CKeyboardScanner unconditionally calls Wire.begin(I2C_SDA, I2C_SCL);
-D I2C_SDA=8
-D I2C_SCL=9
-D INPUTDRIVER_MATRIX_TYPE=1
-D USE_PIN_BUZZER=PIN_BUZZER
-D USE_SX127x
@@ -19,14 +19,14 @@ custom_meshtastic_support_level = 1
custom_meshtastic_display_name = RAK WisMesh Tap V2
custom_meshtastic_images = rak-wismesh-tap-v2.svg
custom_meshtastic_tags = RAK
custom_meshtastic_partition_scheme = 8MB
custom_meshtastic_partition_scheme = 16MB
custom_meshtastic_has_mui = true
extends = esp32s3_base
board = wiscore_rak3312
board_check = true
upload_protocol = esptool
board_build.partitions = default_8MB.csv
board_build.partitions = default_16MB.csv
build_flags =
${esp32s3_base.build_flags}
@@ -53,6 +53,7 @@
#define BATTERY_PIN 1
#define ADC_CHANNEL ADC1_GPIO1_CHANNEL
#define ADC_MULTIPLIER 1.667
#define OCV_ARRAY 4160, 4020, 3940, 3870, 3810, 3760, 3740, 3720, 3680, 3620, 2990
#define PIN_BUZZER 38
+6 -7
View File
@@ -55,9 +55,9 @@ build_flags =
-D HAS_TFT=1
-D USE_I2S_BUZZER
-D RAM_SIZE=5120
-D LV_LVGL_H_INCLUDE_SIMPLE
-D LV_CONF_INCLUDE_SIMPLE
-D LV_COMP_CONF_INCLUDE_SIMPLE
-D LV_LVGL_H_INCLUDE_SIMPLE
-D LV_CONF_INCLUDE_SIMPLE
-D LV_COMP_CONF_INCLUDE_SIMPLE
-D LV_USE_SYSMON=0
-D LV_USE_PROFILER=0
-D LV_USE_PERF_MONITOR=0
@@ -72,6 +72,7 @@ build_flags =
; -D CALIBRATE_TOUCH=0
-D LGFX_SCREEN_WIDTH=240
-D LGFX_SCREEN_HEIGHT=320
-D LGFX_BUFSIZE=153600
-D DISPLAY_SIZE=320x240 ; landscape mode
-D LGFX_DRIVER=LGFX_TDECK
-D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_T_DECK.h\"
@@ -79,13 +80,11 @@ build_flags =
; -D GFX_DRIVER_INC=\"graphics/LVGL/LVGL_T_DECK.h\"
; -D LV_USE_ST7789=1
-D VIEW_320x240
; -D USE_DOUBLE_BUFFER
-D USE_PACKET_API
-D MAP_FULL_REDRAW
-D CUSTOM_TOUCH_DRIVER
; -D CUSTOM_TOUCH_DRIVER
lib_deps =
${env:t-deck.lib_deps}
${device-ui_base.lib_deps}
# renovate: datasource=github-tags depName=bb_captouch packageName=bitbank2/bb_captouch
https://github.com/bitbank2/bb_captouch/archive/refs/tags/1.3.1.zip
;https://github.com/bitbank2/bb_captouch/archive/refs/tags/1.3.1.zip
+1 -1
View File
@@ -2,7 +2,7 @@
[portduino_base]
platform =
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
https://github.com/meshtastic/platform-native/archive/cab4b21d902973e43c938dab3cf4844ba02547ec.zip
https://github.com/meshtastic/platform-native/archive/61067ac3774e4fe27aa9762c72cebea507f116c8.zip
framework = arduino
build_src_filter =
@@ -56,8 +56,8 @@ void initVariant()
digitalWrite(DHT_POWER, HIGH);
pinMode(Battery_POWER, OUTPUT);
digitalWrite(Battery_POWER, HIGH);
pinMode(GPS_POWER, OUTPUT);
digitalWrite(GPS_POWER, HIGH);
pinMode(PIN_GPS_EN, OUTPUT);
digitalWrite(PIN_GPS_EN, HIGH);
}
// called from main-nrf52.cpp during the cpuDeepSleep() function
@@ -74,12 +74,11 @@ void variant_shutdown()
digitalWrite(DHT_POWER, LOW);
digitalWrite(ACC_POWER, LOW);
digitalWrite(Battery_POWER, LOW);
digitalWrite(GPS_POWER, LOW);
// This sets the pin to OUTPUT and LOW for the pins *not* in the if block.
for (int pin = 0; pin < 48; pin++) {
if (pin == PIN_POWER_USB || pin == BUTTON_PIN || pin == PIN_EN1 || pin == PIN_EN2 || pin == DHT_POWER ||
pin == ACC_POWER || pin == Battery_POWER || pin == GPS_POWER || pin == LR1110_SPI_MISO_PIN ||
pin == ACC_POWER || pin == Battery_POWER || pin == PIN_GPS_EN || pin == LR1110_SPI_MISO_PIN ||
pin == LR1110_SPI_MOSI_PIN || pin == LR1110_SPI_SCK_PIN || pin == LR1110_SPI_NSS_PIN || pin == LR1110_BUSY_PIN ||
pin == LR1110_NRESET_PIN || pin == LR1110_IRQ_PIN || pin == GPS_TX_PIN || pin == GPS_RX_PIN || pin == LED_GREEN ||
pin == LED_RED || pin == LED_BLUE) {
@@ -101,4 +100,4 @@ void variant_shutdown()
nrf_gpio_cfg_input(PIN_POWER_USB, NRF_GPIO_PIN_PULLDOWN); // Configure the pin to be woken up as an input
nrf_gpio_pin_sense_t sense2 = NRF_GPIO_PIN_SENSE_HIGH;
nrf_gpio_cfg_sense_set(PIN_POWER_USB, sense2);
}
}
@@ -37,7 +37,8 @@ extern "C" {
// Power Pin
#define NRF_APM
#define GPS_POWER 14
#define PIN_GPS_EN 14
#define GPS_EN_ACTIVE HIGH
#define PIN_POWER_USB 31
#define EXT_PWR_DETECT PIN_POWER_USB
#define PIN_POWER_DONE 24
+1 -1
View File
@@ -1,4 +1,4 @@
[VERSION]
major = 2
minor = 7
build = 25
build = 27