mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-15 07:51:19 +10:00
* Tips robot virtual node / relayer to different LoRa modes & channels
Note that this commit has details hardcoded for the Wellington (NZ)
mesh, and also requires the following patch to the protobufs:
-----
diff --git a/meshtastic/mesh.proto b/meshtastic/mesh.proto
index 03162d8..ec54c99 100644
--- a/meshtastic/mesh.proto
+++ b/meshtastic/mesh.proto
@@ -1393,6 +1393,21 @@ message MeshPacket {
* Set by the firmware internally, clients are not supposed to set this.
*/
uint32 tx_after = 20;
+
+ /*
+ * The modem preset to use fo rthis packet
+ */
+ uint32 modem_preset = 21;
+
+ /*
+ * The frequency slot to use for this packet
+ */
+ uint32 frequency_slot = 22;
+
+ /*
+ * Whether the packet has a nonstandard radio config
+ */
+ bool nonstandard_radio_config = 23;
}
/*
-----
* fix: repair mesh tips CI build
* feat: add MeshBeacon module (Phase 1 — proto + generated code + initial stub)
* feat(beacon): implement broadcaster + listener (phases 2-5)
* feat(beacon): wire RadioLibInterface hooks + admin validation (phases 6-7)
* fix(beacon): fix LocalModuleConfig flat access (no payload_variant), add localonly proto field
* feat(beacon): fix broadcaster inheritance, add preset/region validation + proto cache
- MeshBeaconBroadcastModule now inherits ProtobufModule<meshtastic_MeshBeacon>
(alongside private MeshBeaconModule + OSThread), giving it allocDataPacket()
and setStartDelay() without extra includes.
- Payload cache: rebuildCache() encodes the MeshBeacon protobuf once and stores
it in payloadCache[]/payloadCacheSize; sendBeacon() only calls rebuildCache()
when payloadCacheDirty==true. AdminModule calls invalidateCache() after saving
new config so the next broadcast picks up changes.
- Region/preset validation in handleSetModuleConfig (mesh_beacon_tag):
broadcast_on_preset is validated against the device's current region via
RadioInterface::validateConfigLora(); broadcast_offer_region is validated via
RadioInterface::validateConfigRegion(). Invalid values are zeroed with a
LOG_WARN before saving.
* feat(beacon): add unit tests for MeshBeaconModule and AdminModule configuration validation
* remove old meshtips
* more validation in NodeDB and AdminModule, and userprefs for baked in goodness
* copilot is my gravity
* mmmmm... beacon
* oops
* Enhance unit tests for MeshBeaconModule with detailed validation checks and output formatting
* new lines. Why not?
* finally
* legacy mode activate!
* Update protobufs (#17)
Co-authored-by: NomDeTom <116762865+NomDeTom@users.noreply.github.com>
* better logic, fixed a test
* updated for packet signing
fixed a test
added guards for licensed/ham mode
* channel numbers
* beacon: encrypt on the beacon channel PSK; fix split note
When broadcast_on_channel overrides the primary channel's name/PSK, the
beacon was encrypted with the PRIMARY PSK: perhapsEncode keys encryption
off the primary slot, but the radio-thread channel switch happens only
after encryption. sendBeaconPacket() now installs the beacon channel into
the primary slot for the synchronous duration of send() (cooperative
threading => no interleaving) so encryption/hash use the beacon channel,
then restores it. A shared beaconChannelSettings() helper builds the
channel for both the encrypt-time swap and the RF-time swap so the
key+hash cannot drift.
Also: correct the legacy-split comments (both packets go out on the same
beacon radio settings, not the normal config) and merge the two
consecutive `if (hasText)` blocks in the listener (cppcheck
duplicateCondition).
Tests: add channelPskOverride_swapsBeaconChannelAndRestores and
noChannelOverride_doesNotSwapPrimary; MockRouter snapshots the primary
channel at send() time.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test/beacon: drain toPhoneQueue in tearDown to fix LSan leak abort
The listener delivers received text via MeshService::sendToPhone(), which
enqueues the packet into toPhoneQueue and takes ownership. Nothing dequeues
it in tests, so the three listener tests carrying message text stranded a
MeshPacket each — 1272 bytes / 3 allocations that LeakSanitizer flagged at
process exit, aborting the coverage run (surfaced by pio as [ERRORED] /
SIGHUP even though all 40 assertions passed).
Drain the phone queue in tearDown (getForPhone()/releaseToPool) so the
packets return to packetPool. Suite is now GREEN with no sanitizer abort.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* legacy hop override for zero-hoppers
* ever more beacons
* beacon: comment out broadcast_send_as_node pending further review
Functionality preserved in comments with full signing/has_bitfield notes
for when it is re-enabled. Proto tag 3 retained on the wire.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test/beacon: fix fromIsCustomNodeWhenSet now that send-as-node is disabled
broadcast_send_as_node is commented out; from is always the local node.
Update the test assertion and doc comment to match current behaviour.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Update protobufs (#21)
Co-authored-by: NomDeTom <116762865+NomDeTom@users.noreply.github.com>
* flags for beacons
* beacon: do more with less — slot-index targets + validation
Multi-target beacons embedded a full ChannelSettings in every BroadcastTarget,
blowing ModuleConfig past the 512-byte BLE FromRadio budget so the firmware would
not compile. Targets now reference an existing channel-table slot by channel_index
and the broadcaster resolves it via channels.getByIndex() at TX time. Net effect:
the same multi-target capability for a fraction of the bytes —
FromRadio 609 -> 510 B, MeshBeaconConfig 596 -> 324 B, AdminMessage 615 -> 511 B.
- proto: BroadcastTarget.channel (embedded) -> channel_index (uint32 ref); regen all
generated headers (size constants propagate to admin/localonly/deviceonly/mesh).
- broadcaster: resolve channel_index from the channel table; an out-of-range or blank
slot falls back to the default channel for the target preset rather than borrowing
the primary's name/PSK.
- AdminModule: validate broadcast_targets entries on write (region/preset sanitised
like the single-target fields; channel_index range-checked).
- userPrefs: TARGET_<n>_CHANNEL_{NAME,NUM,PSK} collapse to a single CHANNEL_INDEX.
- docs: two-step (set_channel -> set_module_config) multi-target setup, inline-vs-
reference distinction, and single-/multi-target are equal (not "legacy") options.
- tests: target validation + channel-index resolution incl. blank-slot fallback
(47/47 green on `./bin/run-tests.sh -e native -f test_mesh_beacon`).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NRAF5csgsMn6p1zEcFL8Qz
* throttling after reboot
* address copilot review
* simplify
* fix(beacon): use 0x%08x for node/packet IDs in logs; register test suite
The %#08lx log specifiers passed uint32_t (NodeNum/PacketId) to a %lx
length modifier — undefined behaviour on 64-bit (native test) targets and
non-standard width. Switch to the project-standard 0x%08x. Also bump
test/native-suite-count to 25 for the added test_mesh_beacon suite.
clod helped too
* copilot & clarity
clod helped too
* refactor(beacon): use auto for the sanitized config copy
clod helped too
* fix(beacon): guard empty-payload sends; gate has_mesh_beacon on build flag; document ISR_TX pre-switch
clod helped too
---------
Co-authored-by: Steve Gilberd <steve@erayd.net>
Co-authored-by: Darafei Praliaskouski <me@komzpa.net>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
132 lines
6.9 KiB
C++
132 lines
6.9 KiB
C++
#pragma once
|
||
#include <MeshRadio.h>
|
||
#include <NodeDB.h>
|
||
#include <RadioInterface.h>
|
||
#include <cmath>
|
||
#include <cstdint>
|
||
#include <meshUtils.h>
|
||
#define ONE_DAY 24 * 60 * 60
|
||
#define ONE_MINUTE_MS 60 * 1000
|
||
#define THIRTY_SECONDS_MS 30 * 1000
|
||
#define TWO_SECONDS_MS 2 * 1000
|
||
#define FIVE_SECONDS_MS 5 * 1000
|
||
#define TEN_SECONDS_MS 10 * 1000
|
||
#define MAX_INTERVAL INT32_MAX // FIXME: INT32_MAX to avoid overflow issues with Apple clients but should be UINT32_MAX
|
||
|
||
#define min_default_telemetry_interval_secs IF_ROUTER(ONE_DAY / 2, 30 * 60)
|
||
#define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60)
|
||
#define default_telemetry_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 60 * 60)
|
||
#define default_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 60 * 60)
|
||
#define default_broadcast_smart_minimum_interval_secs 5 * 60
|
||
// Floor for our own position broadcasts when stationary (unchanged beyond the broadcast
|
||
// precision) or fixed_position: identical positions get deduped by traffic management anyway.
|
||
#define default_position_stationary_broadcast_secs (12 * 60 * 60)
|
||
#define min_default_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 60 * 60)
|
||
#define min_default_broadcast_smart_minimum_interval_secs 5 * 60
|
||
#define default_wait_bluetooth_secs IF_ROUTER(1, 60)
|
||
#define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep
|
||
#define default_ls_secs IF_ROUTER(ONE_DAY, 5 * 60)
|
||
#define default_min_wake_secs 10
|
||
#define default_screen_on_secs IF_ROUTER(1, 60 * 10)
|
||
#define default_node_info_broadcast_secs 3 * 60 * 60
|
||
#define default_neighbor_info_broadcast_secs 6 * 60 * 60
|
||
#define default_mesh_beacon_min_broadcast_interval_secs 3600
|
||
#define min_node_info_broadcast_secs 60 * 60 // No regular broadcasts of more than once an hour
|
||
#define min_neighbor_info_broadcast_secs 4 * 60 * 60
|
||
#define default_map_publish_interval_secs 60 * 60
|
||
|
||
enum class TrafficType { POSITION, TELEMETRY };
|
||
|
||
// Traffic management defaults
|
||
#define default_traffic_mgmt_position_precision_bits 19 // ~90m grid cells (±45m)
|
||
#define default_traffic_mgmt_position_min_interval_secs (11 * 60 * 60) // 11 hours between identical positions
|
||
// Role cap: tracker-role origins may refresh a duplicate position this often (vs the 11h default).
|
||
#define default_traffic_mgmt_tracker_position_min_interval_secs (60 * 60) // 1 hour
|
||
// Role cap: lost-and-found origins may refresh a duplicate position this often, so a lost
|
||
// device updates frequently without flooding. (Quantised to the dedup tick: ~2 ticks.)
|
||
// Unlike before, lost-and-found is NOT exempt from the relayed precision clamp.
|
||
#define default_traffic_mgmt_lost_and_found_position_min_interval_secs (15 * 60) // 15 minutes
|
||
|
||
// Hop scaling defaults
|
||
#define default_hop_scaling_min_target_nodes 40 // walk threshold: first hop reaching this cumulative count
|
||
#define default_hop_scaling_max_target_nodes 80 // generous extension ceiling (2 × min)
|
||
#define default_hop_scaling_min_target_nodes_floor 5 // minimum allowed min_target_nodes
|
||
#define default_hop_scaling_max_target_nodes_ceiling 512 // maximum allowed max_target_nodes
|
||
|
||
#ifdef USERPREFS_RINGTONE_NAG_SECS
|
||
#define default_ringtone_nag_secs USERPREFS_RINGTONE_NAG_SECS
|
||
#else
|
||
#define default_ringtone_nag_secs 15
|
||
#endif
|
||
#define default_network_ipv6_enabled false
|
||
|
||
#define default_mqtt_address "mqtt.meshtastic.org"
|
||
#define default_mqtt_username "meshdev"
|
||
#define default_mqtt_password "large4cats"
|
||
#define default_mqtt_root "msh"
|
||
#define default_mqtt_encryption_enabled true
|
||
#define default_mqtt_tls_enabled false
|
||
|
||
#define IF_ROUTER(routerVal, normalVal) \
|
||
((config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || \
|
||
config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE) \
|
||
? (routerVal) \
|
||
: (normalVal))
|
||
|
||
class Default
|
||
{
|
||
public:
|
||
static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval);
|
||
static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval);
|
||
static uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue);
|
||
// Note: numOnlineNodes uses uint32_t to match the public API and allow flexibility,
|
||
// even though internal node counts use uint16_t (max 65535 nodes)
|
||
static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes);
|
||
static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes,
|
||
TrafficType type);
|
||
static uint8_t getConfiguredOrDefaultHopLimit(uint8_t configured);
|
||
static uint32_t getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue);
|
||
|
||
private:
|
||
// Note: Kept as uint32_t to match the public API parameter type
|
||
static float congestionScalingCoefficient(uint32_t numOnlineNodes)
|
||
{
|
||
if (numOnlineNodes <= 40) {
|
||
return 1.0;
|
||
} else {
|
||
// Resolve SF and BW from preset or manual config
|
||
// When use_preset is true, config.lora.spread_factor and bandwidth may be 0
|
||
// because applyModemConfig() sets them on RadioInterface, not on config.lora
|
||
float bwKHz;
|
||
uint8_t sf;
|
||
uint8_t cr;
|
||
if (config.lora.use_preset) {
|
||
modemPresetToParams(config.lora.modem_preset, false, bwKHz, sf, cr);
|
||
} else {
|
||
sf = config.lora.spread_factor;
|
||
bwKHz = bwCodeToKHz(config.lora.bandwidth);
|
||
}
|
||
|
||
// Guard against invalid values
|
||
sf = clampSpreadFactor(sf);
|
||
bwKHz = clampBandwidthKHz(bwKHz);
|
||
|
||
// throttlingFactor = 2^SF / (BW_in_kHz * scaling_divisor)
|
||
// With scaling_divisor=100:
|
||
// In SF11 and BW=250khz (longfast), this gives 0.08192 rather than the original 0.075
|
||
// In SF10 and BW=250khz (mediumslow), this gives 0.04096 rather than the original 0.04
|
||
// In SF9 and BW=250khz (mediumfast), this gives 0.02048 rather than the original 0.02
|
||
// In SF7 and BW=250khz (shortfast), this gives 0.00512 rather than the original 0.01
|
||
float throttlingFactor = static_cast<float>(pow_of_2(sf)) / (bwKHz * 100.0f);
|
||
|
||
#if USERPREFS_EVENT_MODE
|
||
// If we are in event mode, scale down the throttling factor by 4
|
||
throttlingFactor = static_cast<float>(pow_of_2(sf)) / (bwKHz * 25.0f);
|
||
#endif
|
||
|
||
// Scaling up traffic based on number of nodes over 40
|
||
int nodesOverForty = (numOnlineNodes - 40);
|
||
return 1.0 + (nodesOverForty * throttlingFactor); // Each number of online node scales by throttle factor
|
||
}
|
||
}
|
||
}; |