mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-02 08:00:38 +00:00
Compare commits
33 Commits
6c02f82d87
...
refactor-n
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90e2083d2c | ||
|
|
af1d4e08ff | ||
|
|
2032ff1c32 | ||
|
|
5910cc2e26 | ||
|
|
aa72e397f2 | ||
|
|
c55bea8460 | ||
|
|
aa605fc4a2 | ||
|
|
d75680a2dd | ||
|
|
decd58cd5c | ||
|
|
e691bd9732 | ||
|
|
6bad81f8dd | ||
|
|
69b9977fc1 | ||
|
|
8e63dcf59a | ||
|
|
042543eb25 | ||
|
|
928739e0fb | ||
|
|
65c418d4e1 | ||
|
|
c3a69a2742 | ||
|
|
bd4bcb94f0 | ||
|
|
eeaafda62a | ||
|
|
6e9fd189b4 | ||
|
|
3f40916223 | ||
|
|
1b4925bd07 | ||
|
|
0828c445fb | ||
|
|
90584359e4 | ||
|
|
8a43741589 | ||
|
|
d772cd4431 | ||
|
|
b7b8071056 | ||
|
|
4a3d28f06b | ||
|
|
0f4210d2e8 | ||
|
|
dc6e109329 | ||
|
|
10141b2562 | ||
|
|
770085ddf7 | ||
|
|
833f950edc |
1
.github/actionlint.yaml
vendored
1
.github/actionlint.yaml
vendored
@@ -2,4 +2,5 @@
|
||||
self-hosted-runner:
|
||||
# Labels of self-hosted runner in array of strings.
|
||||
labels:
|
||||
- arctastic
|
||||
- test-runner
|
||||
|
||||
3
.github/workflows/build_firmware.yml
vendored
3
.github/workflows/build_firmware.yml
vendored
@@ -18,7 +18,8 @@ permissions: read-all
|
||||
jobs:
|
||||
pio-build:
|
||||
name: build-${{ inputs.platform }}
|
||||
runs-on: ubuntu-24.04
|
||||
# Use 'arctastic' self-hosted runner pool when building in the main repo
|
||||
runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }}
|
||||
outputs:
|
||||
artifact-id: ${{ steps.upload.outputs.artifact-id }}
|
||||
steps:
|
||||
|
||||
4
.github/workflows/build_one_target.yml
vendored
4
.github/workflows/build_one_target.yml
vendored
@@ -139,8 +139,8 @@ jobs:
|
||||
|
||||
- name: Device scripts permissions
|
||||
run: |
|
||||
chmod +x ./output/device-install.sh
|
||||
chmod +x ./output/device-update.sh
|
||||
chmod +x ./output/device-install.sh || true
|
||||
chmod +x ./output/device-update.sh || true
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
12
.github/workflows/main_matrix.yml
vendored
12
.github/workflows/main_matrix.yml
vendored
@@ -207,8 +207,8 @@ jobs:
|
||||
|
||||
- name: Device scripts permissions
|
||||
run: |
|
||||
chmod +x ./output/device-install.sh
|
||||
chmod +x ./output/device-update.sh
|
||||
chmod +x ./output/device-install.sh || true
|
||||
chmod +x ./output/device-update.sh || true
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
@@ -280,9 +280,9 @@ jobs:
|
||||
|
||||
- name: Generate Release manifest
|
||||
run: |
|
||||
jq -n --arg ver "${{ needs.version.outputs.long }}" --arg targets "${{ toJson(needs.setup.outputs.all) }}" '{
|
||||
jq -n --arg ver "${{ needs.version.outputs.long }}" --argjson targets ${{ toJson(needs.setup.outputs.all) }} '{
|
||||
"version": $ver,
|
||||
"targets": ($targets | fromjson)
|
||||
"targets": $targets
|
||||
}' > firmware-${{ needs.version.outputs.long }}.json
|
||||
|
||||
- name: Save Release manifest artifact
|
||||
@@ -338,8 +338,8 @@ jobs:
|
||||
|
||||
- name: Device scripts permissions
|
||||
run: |
|
||||
chmod +x ./output/device-install.sh
|
||||
chmod +x ./output/device-update.sh
|
||||
chmod +x ./output/device-install.sh || true
|
||||
chmod +x ./output/device-update.sh || true
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
8
.github/workflows/merge_queue.yml
vendored
8
.github/workflows/merge_queue.yml
vendored
@@ -188,8 +188,8 @@ jobs:
|
||||
|
||||
- name: Device scripts permissions
|
||||
run: |
|
||||
chmod +x ./output/device-install.sh
|
||||
chmod +x ./output/device-update.sh
|
||||
chmod +x ./output/device-install.sh || true
|
||||
chmod +x ./output/device-update.sh || true
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
@@ -303,8 +303,8 @@ jobs:
|
||||
|
||||
- name: Device scripts permissions
|
||||
run: |
|
||||
chmod +x ./output/device-install.sh
|
||||
chmod +x ./output/device-update.sh
|
||||
chmod +x ./output/device-install.sh || true
|
||||
chmod +x ./output/device-update.sh || true
|
||||
|
||||
- name: Zip firmware
|
||||
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
|
||||
|
||||
2
.github/workflows/pr_enforce_labels.yml
vendored
2
.github/workflows/pr_enforce_labels.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
with:
|
||||
script: |
|
||||
const labels = context.payload.pull_request.labels.map(label => label.name);
|
||||
const requiredLabels = ['bugfix', 'enhancement', 'hardware-support', 'dependencies', 'submodules', 'github_actions', 'trunk'];
|
||||
const requiredLabels = ['bugfix', 'enhancement', 'hardware-support', 'dependencies', 'submodules', 'github_actions', 'trunk', 'cleanup'];
|
||||
const hasRequiredLabel = labels.some(label => requiredLabels.includes(label));
|
||||
if (!hasRequiredLabel) {
|
||||
core.setFailed(`PR must have at least one of the following labels before it can be merged: ${requiredLabels.join(', ')}.`);
|
||||
|
||||
2
.github/workflows/stale_bot.yml
vendored
2
.github/workflows/stale_bot.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Stale PR+Issues
|
||||
uses: actions/stale@v10.1.0
|
||||
uses: actions/stale@v10.1.1
|
||||
with:
|
||||
days-before-stale: 45
|
||||
stale-issue-message: This issue has not had any comment or update in the last month. If it is still relevant, please post update comments. If no comments are made, this issue will be closed automagically in 7 days.
|
||||
|
||||
@@ -9,9 +9,9 @@ plugins:
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.495
|
||||
- renovate@42.27.1
|
||||
- prettier@3.7.3
|
||||
- trufflehog@3.91.1
|
||||
- renovate@42.30.4
|
||||
- prettier@3.7.4
|
||||
- trufflehog@3.91.2
|
||||
- yamllint@1.37.1
|
||||
- bandit@1.9.2
|
||||
- trivy@0.67.2
|
||||
|
||||
@@ -28,7 +28,7 @@ RUN bash ./bin/build-native.sh "$PIO_ENV" && \
|
||||
|
||||
# ##### PRODUCTION BUILD #############
|
||||
|
||||
FROM alpine:3.22
|
||||
FROM alpine:3.23
|
||||
LABEL org.opencontainers.image.title="Meshtastic" \
|
||||
org.opencontainers.image.description="Alpine Meshtastic daemon" \
|
||||
org.opencontainers.image.url="https://meshtastic.org" \
|
||||
|
||||
@@ -75,7 +75,7 @@ TOOLS = {
|
||||
}
|
||||
|
||||
BACKTRACE_REGEX = re.compile(
|
||||
r"(?:\s+(0x40[0-2](?:\d|[a-f]|[A-F]){5}):0x(?:\d|[a-f]|[A-F]){8})\b"
|
||||
r"\b(0x4[0-9a-fA-F]{7,8}):0x[0-9a-fA-F]{8}\b"
|
||||
)
|
||||
EXCEPTION_REGEX = re.compile("^Exception \\((?P<exc>[0-9]*)\\):$")
|
||||
COUNTER_REGEX = re.compile(
|
||||
@@ -89,7 +89,7 @@ POINTER_REGEX = re.compile(
|
||||
STACK_BEGIN = ">>>stack>>>"
|
||||
STACK_END = "<<<stack<<<"
|
||||
STACK_REGEX = re.compile(
|
||||
"^(?P<off>[0-9a-f]+):\W+(?P<c1>[0-9a-f]+) (?P<c2>[0-9a-f]+) (?P<c3>[0-9a-f]+) (?P<c4>[0-9a-f]+)(\W.*)?$"
|
||||
r"^(?P<off>[0-9a-f]+):\W+(?P<c1>[0-9a-f]+) (?P<c2>[0-9a-f]+) (?P<c3>[0-9a-f]+) (?P<c4>[0-9a-f]+)(\W.*)?$"
|
||||
)
|
||||
|
||||
StackLine = namedtuple("StackLine", ["offset", "content"])
|
||||
@@ -223,7 +223,7 @@ class AddressResolver(object):
|
||||
if match is None:
|
||||
if last is not None and line.startswith("(inlined by)"):
|
||||
line = line[12:].strip()
|
||||
self._address_map[last] += "\n \-> inlined by: " + line
|
||||
self._address_map[last] += "\n \\-> inlined by: " + line
|
||||
continue
|
||||
|
||||
if match.group("result") == "?? ??:0":
|
||||
|
||||
@@ -123,7 +123,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/3bf332240416c5cb8c919fac2a0ec7260eb3be75.zip
|
||||
https://github.com/meshtastic/device-ui/archive/4fb5f24787caa841b58dbf623a52c4c5861d6722.zip
|
||||
|
||||
; Common libs for environmental measurements in telemetry module
|
||||
[environmental_base]
|
||||
@@ -184,8 +184,8 @@ lib_deps =
|
||||
dfrobot/DFRobot_BMM150@1.0.0
|
||||
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
|
||||
adafruit/Adafruit TSL2561@1.1.2
|
||||
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/BH1750_WE@^1.1.10
|
||||
wollewald/BH1750_WE@^1.1.10
|
||||
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/library/BH1750_WE
|
||||
wollewald/BH1750_WE@1.1.10
|
||||
|
||||
; (not included in native / portduino)
|
||||
[environmental_extra]
|
||||
@@ -207,7 +207,7 @@ lib_deps =
|
||||
# renovate: datasource=custom.pio depName=SparkFun Qwiic Scale NAU7802 packageName=sparkfun/library/SparkFun Qwiic Scale NAU7802 Arduino Library
|
||||
sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6
|
||||
# renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
|
||||
ClosedCube OPT3001@1.1.2
|
||||
closedcube/ClosedCube OPT3001@1.1.2
|
||||
# renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2
|
||||
boschsensortec/bsec2@1.10.2610
|
||||
# renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
|
||||
|
||||
Submodule protobufs updated: 52fa252f1e...4095e59890
@@ -10,7 +10,10 @@
|
||||
*/
|
||||
#include "FSCommon.h"
|
||||
#include "SPILock.h"
|
||||
#include "SafeFile.h"
|
||||
#include "configuration.h"
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
|
||||
// Software SPI is used by MUI so disable SD card here until it's also implemented
|
||||
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI)
|
||||
@@ -335,4 +338,63 @@ 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
|
||||
}
|
||||
|
||||
/** Load a protobuf from a file, return LoadFileResult */
|
||||
LoadFileResult loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct)
|
||||
{
|
||||
LoadFileResult state = LoadFileResult::OTHER_FAILURE;
|
||||
#ifdef FSCom
|
||||
concurrency::LockGuard g(spiLock);
|
||||
|
||||
auto f = FSCom.open(filename, FILE_O_READ);
|
||||
|
||||
if (f) {
|
||||
LOG_INFO("Load %s", filename);
|
||||
pb_istream_t stream = {&readcb, &f, protoSize};
|
||||
if (fields != &meshtastic_NodeDatabase_msg) // contains a vector object
|
||||
memset(dest_struct, 0, objSize);
|
||||
if (!pb_decode(&stream, fields, dest_struct)) {
|
||||
LOG_ERROR("Error: can't decode protobuf %s", PB_GET_ERROR(&stream));
|
||||
state = LoadFileResult::DECODE_FAILED;
|
||||
} else {
|
||||
LOG_INFO("Loaded %s successfully", filename);
|
||||
state = LoadFileResult::LOAD_SUCCESS;
|
||||
}
|
||||
f.close();
|
||||
} else {
|
||||
LOG_ERROR("Could not open / read %s", filename);
|
||||
}
|
||||
#else
|
||||
LOG_ERROR("ERROR: Filesystem not implemented");
|
||||
state = LoadFileResult::NO_FILESYSTEM;
|
||||
#endif
|
||||
return state;
|
||||
}
|
||||
|
||||
/** Save a protobuf from a file, return true for success */
|
||||
bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct, bool fullAtomic)
|
||||
{
|
||||
bool okay = false;
|
||||
#ifdef FSCom
|
||||
auto f = SafeFile(filename, fullAtomic);
|
||||
|
||||
LOG_INFO("Save %s", filename);
|
||||
pb_ostream_t stream = {&writecb, static_cast<Print *>(&f), protoSize};
|
||||
|
||||
if (!pb_encode(&stream, fields, dest_struct)) {
|
||||
LOG_ERROR("Error: can't encode protobuf %s", PB_GET_ERROR(&stream));
|
||||
} else {
|
||||
okay = true;
|
||||
}
|
||||
|
||||
bool writeSucceeded = f.close();
|
||||
|
||||
if (!okay || !writeSucceeded) {
|
||||
LOG_ERROR("Can't write prefs!");
|
||||
}
|
||||
#else
|
||||
LOG_ERROR("ERROR: Filesystem not implemented");
|
||||
#endif
|
||||
return okay;
|
||||
}
|
||||
@@ -3,6 +3,19 @@
|
||||
#include "configuration.h"
|
||||
#include <vector>
|
||||
|
||||
enum LoadFileResult {
|
||||
// Successfully opened the file
|
||||
LOAD_SUCCESS = 1,
|
||||
// File does not exist
|
||||
NOT_FOUND = 2,
|
||||
// Device does not have a filesystem
|
||||
NO_FILESYSTEM = 3,
|
||||
// File exists, but could not decode protobufs
|
||||
DECODE_FAILED = 4,
|
||||
// File exists, but open failed for some reason
|
||||
OTHER_FAILURE = 5
|
||||
};
|
||||
|
||||
// Cross platform filesystem API
|
||||
|
||||
#if defined(ARCH_PORTDUINO)
|
||||
@@ -55,4 +68,8 @@ bool renameFile(const char *pathFrom, const char *pathTo);
|
||||
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels);
|
||||
void listDir(const char *dirname, uint8_t levels, bool del = false);
|
||||
void rmDir(const char *dirname);
|
||||
void setupSDCard();
|
||||
void setupSDCard();
|
||||
LoadFileResult loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct);
|
||||
|
||||
bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct,
|
||||
bool fullAtomic = true);
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "configuration.h"
|
||||
#if HAS_SCREEN
|
||||
#include "ClockRenderer.h"
|
||||
#include "FSCommon.h"
|
||||
#include "GPS.h"
|
||||
#include "MenuHandler.h"
|
||||
#include "MeshRadio.h"
|
||||
@@ -1041,12 +1042,13 @@ void menuHandler::switchToMUIMenu()
|
||||
|
||||
void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
||||
{
|
||||
static const char *optionsArray[] = {"Back", "Default", "Meshtastic Green", "Yellow", "Red", "Orange", "Purple", "Teal",
|
||||
"Pink", "White"};
|
||||
static const char *optionsArray[] = {
|
||||
"Back", "Default", "Meshtastic Green", "Yellow", "Red", "Orange", "Purple", "Blue", "Teal", "Cyan", "Ice", "Pink",
|
||||
"White", "Gray"};
|
||||
BannerOverlayOptions bannerOptions;
|
||||
bannerOptions.message = "Select Screen Color";
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
bannerOptions.optionsCount = 10;
|
||||
bannerOptions.optionsCount = 14;
|
||||
bannerOptions.bannerCallback = [display](int selected) -> void {
|
||||
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \
|
||||
HAS_TFT || defined(HACKADAY_COMMUNICATOR)
|
||||
@@ -1082,20 +1084,40 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
|
||||
TFT_MESH_g = 153;
|
||||
TFT_MESH_b = 255;
|
||||
} else if (selected == 7) {
|
||||
LOG_INFO("Setting color to Teal");
|
||||
TFT_MESH_r = 64;
|
||||
TFT_MESH_g = 224;
|
||||
TFT_MESH_b = 208;
|
||||
LOG_INFO("Setting color to Blue");
|
||||
TFT_MESH_r = 0;
|
||||
TFT_MESH_g = 0;
|
||||
TFT_MESH_b = 255;
|
||||
} else if (selected == 8) {
|
||||
LOG_INFO("Setting color to Teal");
|
||||
TFT_MESH_r = 16;
|
||||
TFT_MESH_g = 102;
|
||||
TFT_MESH_b = 102;
|
||||
} else if (selected == 9) {
|
||||
LOG_INFO("Setting color to Cyan");
|
||||
TFT_MESH_r = 0;
|
||||
TFT_MESH_g = 255;
|
||||
TFT_MESH_b = 255;
|
||||
} else if (selected == 10) {
|
||||
LOG_INFO("Setting color to Ice");
|
||||
TFT_MESH_r = 173;
|
||||
TFT_MESH_g = 216;
|
||||
TFT_MESH_b = 230;
|
||||
} else if (selected == 11) {
|
||||
LOG_INFO("Setting color to Pink");
|
||||
TFT_MESH_r = 255;
|
||||
TFT_MESH_g = 105;
|
||||
TFT_MESH_b = 180;
|
||||
} else if (selected == 9) {
|
||||
} else if (selected == 12) {
|
||||
LOG_INFO("Setting color to White");
|
||||
TFT_MESH_r = 255;
|
||||
TFT_MESH_g = 255;
|
||||
TFT_MESH_b = 255;
|
||||
} else if (selected == 13) {
|
||||
LOG_INFO("Setting color to Gray");
|
||||
TFT_MESH_r = 128;
|
||||
TFT_MESH_g = 128;
|
||||
TFT_MESH_b = 128;
|
||||
} else {
|
||||
menuQueue = system_base_menu;
|
||||
screen->runNow();
|
||||
@@ -1768,7 +1790,7 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
|
||||
|
||||
void menuHandler::saveUIConfig()
|
||||
{
|
||||
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
|
||||
saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
|
||||
@@ -62,22 +62,12 @@ void InkHUD::HeardApplet::populateFromNodeDB()
|
||||
{
|
||||
// Fill a collection with pointers to each node in db
|
||||
std::vector<meshtastic_NodeInfoLite *> ordered;
|
||||
for (auto mn = nodeDB->meshNodes->begin(); mn != nodeDB->meshNodes->end(); ++mn) {
|
||||
// Only copy if valid, and not our own node
|
||||
for (int i = 1; i < maxCards(); i++) {
|
||||
auto mn = nodeDB->getMeshNodeByIndex(i);
|
||||
if (mn->num != 0 && mn->num != nodeDB->getNodeNum())
|
||||
ordered.push_back(&*mn);
|
||||
}
|
||||
|
||||
// Sort the collection by age
|
||||
std::sort(ordered.begin(), ordered.end(), [](meshtastic_NodeInfoLite *top, meshtastic_NodeInfoLite *bottom) -> bool {
|
||||
return (top->last_heard > bottom->last_heard);
|
||||
});
|
||||
|
||||
// Keep the most recent entries only
|
||||
// Just enough to fill the screen
|
||||
if (ordered.size() > maxCards())
|
||||
ordered.resize(maxCards());
|
||||
|
||||
// Create card info for these (stale) node observations
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
for (meshtastic_NodeInfoLite *node : ordered) {
|
||||
|
||||
@@ -53,9 +53,9 @@ void CannedMessageStore::load()
|
||||
|
||||
// Attempt to load the bulk canned message data from flash
|
||||
meshtastic_CannedMessageModuleConfig cannedMessageModuleConfig;
|
||||
LoadFileResult result = nodeDB->loadProto("/prefs/cannedConf.proto", meshtastic_CannedMessageModuleConfig_size,
|
||||
sizeof(meshtastic_CannedMessageModuleConfig),
|
||||
&meshtastic_CannedMessageModuleConfig_msg, &cannedMessageModuleConfig);
|
||||
LoadFileResult result = loadProto("/prefs/cannedConf.proto", meshtastic_CannedMessageModuleConfig_size,
|
||||
sizeof(meshtastic_CannedMessageModuleConfig), &meshtastic_CannedMessageModuleConfig_msg,
|
||||
&cannedMessageModuleConfig);
|
||||
|
||||
// Abort if nothing to load
|
||||
if (result != LoadFileResult::LOAD_SUCCESS || strlen(cannedMessageModuleConfig.messages) == 0)
|
||||
@@ -129,8 +129,8 @@ void CannedMessageStore::handleSet(const meshtastic_AdminMessage *request)
|
||||
#endif
|
||||
|
||||
// Write to flash
|
||||
nodeDB->saveProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size,
|
||||
&meshtastic_CannedMessageModuleConfig_msg, &cannedMessageModuleConfig);
|
||||
saveProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size, &meshtastic_CannedMessageModuleConfig_msg,
|
||||
&cannedMessageModuleConfig);
|
||||
|
||||
// Reload from flash, to update the canned messages in RAM
|
||||
// (This is a lazy way to handle it)
|
||||
|
||||
@@ -439,6 +439,11 @@ void setup()
|
||||
|
||||
LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n");
|
||||
|
||||
#if defined(ARCH_ESP32) && defined(BOARD_HAS_PSRAM)
|
||||
// use PSRAM for malloc calls > 256 bytes
|
||||
heap_caps_malloc_extmem_enable(256);
|
||||
#endif
|
||||
|
||||
#if defined(DEBUG_MUTE) && defined(DEBUG_PORT)
|
||||
DEBUG_PORT.printf("\r\n\r\n//\\ E S H T /\\ S T / C\r\n");
|
||||
DEBUG_PORT.printf("Version %s for %s from %s\r\n", optstr(APP_VERSION), optstr(APP_ENV), optstr(APP_REPO));
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "SPILock.h"
|
||||
#include "SafeFile.h"
|
||||
#include "TypeConversions.h"
|
||||
#include "error.h"
|
||||
#include "main.h"
|
||||
@@ -314,7 +313,7 @@ NodeDB::NodeDB()
|
||||
LOG_DEBUG("Number of Device Reboots: %d", myNodeInfo.reboot_count);
|
||||
#endif
|
||||
|
||||
resetRadioConfig(); // If bogus settings got saved, then fix them
|
||||
_resetRadioConfig(); // If bogus settings got saved, then fix them
|
||||
// nodeDB->LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d", config.lora.region, myNodeInfo.my_node_num, numMeshNodes);
|
||||
|
||||
// Uncomment below to always enable UDP broadcasts
|
||||
@@ -425,13 +424,13 @@ NodeDB::NodeDB()
|
||||
config.has_position = true;
|
||||
info->has_position = true;
|
||||
info->position = TypeConversions::ConvertToPositionLite(fixedGPS);
|
||||
nodeDB->setLocalPosition(fixedGPS);
|
||||
nodeDB->_setLocalPosition(fixedGPS);
|
||||
config.position.fixed_position = true;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
sortMeshDB();
|
||||
saveToDisk(saveWhat);
|
||||
_saveToDisk(saveWhat);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,7 +459,7 @@ bool isBroadcast(uint32_t dest)
|
||||
return dest == NODENUM_BROADCAST || dest == NODENUM_BROADCAST_NO_LORA;
|
||||
}
|
||||
|
||||
void NodeDB::resetRadioConfig(bool is_fresh_install)
|
||||
void NodeDB::_resetRadioConfig(bool is_fresh_install)
|
||||
{
|
||||
if (is_fresh_install) {
|
||||
radioGeneration++;
|
||||
@@ -480,6 +479,7 @@ void NodeDB::resetRadioConfig(bool is_fresh_install)
|
||||
|
||||
bool NodeDB::factoryReset(bool eraseBleBonds)
|
||||
{
|
||||
FUNCTION_START("factoryReset");
|
||||
LOG_INFO("Perform factory reset!");
|
||||
// first, remove the "/prefs" (this removes most prefs)
|
||||
spiLock->lock();
|
||||
@@ -498,7 +498,7 @@ bool NodeDB::factoryReset(bool eraseBleBonds)
|
||||
installDefaultModuleConfig();
|
||||
installDefaultChannels();
|
||||
// third, write everything to disk
|
||||
saveToDisk();
|
||||
_saveToDisk();
|
||||
if (eraseBleBonds) {
|
||||
LOG_INFO("Erase BLE bonds");
|
||||
#ifdef ARCH_ESP32
|
||||
@@ -513,6 +513,7 @@ bool NodeDB::factoryReset(bool eraseBleBonds)
|
||||
Bluefruit.Central.clearBonds();
|
||||
#endif
|
||||
}
|
||||
FUNCTION_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -649,7 +650,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
||||
config.device.node_info_broadcast_secs = default_node_info_broadcast_secs;
|
||||
config.security.serial_enabled = true;
|
||||
config.security.admin_channel_enabled = false;
|
||||
resetRadioConfig(true); // This also triggers NodeInfo/Position requests since we're fresh
|
||||
_resetRadioConfig(true); // This also triggers NodeInfo/Position requests since we're fresh
|
||||
strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32);
|
||||
|
||||
#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3) || defined(SENSECAP_INDICATOR) || \
|
||||
@@ -746,7 +747,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
||||
|
||||
#ifdef USERPREFS_CONFIG_DEVICE_ROLE
|
||||
// Apply role-specific defaults when role is set via user preferences
|
||||
installRoleDefaults(config.device.role);
|
||||
_installRoleDefaults(config.device.role);
|
||||
#endif
|
||||
|
||||
initConfigIntervals();
|
||||
@@ -904,7 +905,7 @@ void NodeDB::installDefaultModuleConfig()
|
||||
initModuleConfigIntervals();
|
||||
}
|
||||
|
||||
void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
|
||||
void NodeDB::_installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
|
||||
{
|
||||
if (role == meshtastic_Config_DeviceConfig_Role_ROUTER) {
|
||||
initConfigIntervals();
|
||||
@@ -990,6 +991,7 @@ void NodeDB::installDefaultChannels()
|
||||
|
||||
void NodeDB::resetNodes(bool keepFavorites)
|
||||
{
|
||||
FUNCTION_START("resetNodes");
|
||||
if (!config.position.fixed_position)
|
||||
clearLocalPosition();
|
||||
numMeshNodes = 1;
|
||||
@@ -1013,10 +1015,12 @@ void NodeDB::resetNodes(bool keepFavorites)
|
||||
saveDeviceStateToDisk();
|
||||
if (neighborInfoModule && moduleConfig.neighbor_info.enabled)
|
||||
neighborInfoModule->resetNeighbors();
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
void NodeDB::removeNodeByNum(NodeNum nodeNum)
|
||||
{
|
||||
FUNCTION_START("removeNodeByNum");
|
||||
int newPos = 0, removed = 0;
|
||||
for (int i = 0; i < numMeshNodes; i++) {
|
||||
if (meshNodes->at(i).num != nodeNum)
|
||||
@@ -1029,16 +1033,17 @@ void NodeDB::removeNodeByNum(NodeNum nodeNum)
|
||||
meshtastic_NodeInfoLite());
|
||||
LOG_DEBUG("NodeDB::removeNodeByNum purged %d entries. Save changes", removed);
|
||||
saveNodeDatabaseToDisk();
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
void NodeDB::clearLocalPosition()
|
||||
void NodeDB::_clearLocalPosition()
|
||||
{
|
||||
meshtastic_NodeInfoLite *node = getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeInfoLite *node = _getMeshNode(nodeDB->getNodeNum());
|
||||
node->position.latitude_i = 0;
|
||||
node->position.longitude_i = 0;
|
||||
node->position.altitude = 0;
|
||||
node->position.time = 0;
|
||||
setLocalPosition(meshtastic_Position_init_default);
|
||||
_setLocalPosition(meshtastic_Position_init_default);
|
||||
}
|
||||
|
||||
void NodeDB::cleanupMeshDB()
|
||||
@@ -1114,7 +1119,7 @@ void NodeDB::pickNewNodeNum()
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite *found;
|
||||
while (((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0) ||
|
||||
while (((found = _getMeshNode(nodeNum)) && memcmp(found->user.macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0) ||
|
||||
(nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED)) {
|
||||
NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice
|
||||
if (found)
|
||||
@@ -1128,39 +1133,6 @@ void NodeDB::pickNewNodeNum()
|
||||
myNodeInfo.my_node_num = nodeNum;
|
||||
}
|
||||
|
||||
/** Load a protobuf from a file, return LoadFileResult */
|
||||
LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields,
|
||||
void *dest_struct)
|
||||
{
|
||||
LoadFileResult state = LoadFileResult::OTHER_FAILURE;
|
||||
#ifdef FSCom
|
||||
concurrency::LockGuard g(spiLock);
|
||||
|
||||
auto f = FSCom.open(filename, FILE_O_READ);
|
||||
|
||||
if (f) {
|
||||
LOG_INFO("Load %s", filename);
|
||||
pb_istream_t stream = {&readcb, &f, protoSize};
|
||||
if (fields != &meshtastic_NodeDatabase_msg) // contains a vector object
|
||||
memset(dest_struct, 0, objSize);
|
||||
if (!pb_decode(&stream, fields, dest_struct)) {
|
||||
LOG_ERROR("Error: can't decode protobuf %s", PB_GET_ERROR(&stream));
|
||||
state = LoadFileResult::DECODE_FAILED;
|
||||
} else {
|
||||
LOG_INFO("Loaded %s successfully", filename);
|
||||
state = LoadFileResult::LOAD_SUCCESS;
|
||||
}
|
||||
f.close();
|
||||
} else {
|
||||
LOG_ERROR("Could not open / read %s", filename);
|
||||
}
|
||||
#else
|
||||
LOG_ERROR("ERROR: Filesystem not implemented");
|
||||
state = LoadFileResult::NO_FILESYSTEM;
|
||||
#endif
|
||||
return state;
|
||||
}
|
||||
|
||||
void NodeDB::loadFromDisk()
|
||||
{
|
||||
// Mark the current device state as completely unusable, so that if we fail reading the entire file from
|
||||
@@ -1260,7 +1232,7 @@ void NodeDB::loadFromDisk()
|
||||
if (backupSecurity.private_key.size > 0) {
|
||||
LOG_DEBUG("Restoring backup of security config");
|
||||
config.security = backupSecurity;
|
||||
saveToDisk(SEGMENT_CONFIG);
|
||||
_saveToDisk(SEGMENT_CONFIG);
|
||||
}
|
||||
|
||||
// Make sure we load hard coded admin keys even when the configuration file has none.
|
||||
@@ -1311,7 +1283,7 @@ void NodeDB::loadFromDisk()
|
||||
if (numAdminKeys > 0) {
|
||||
LOG_INFO("Saving %d hard coded admin keys.", numAdminKeys);
|
||||
config.security.admin_key_count = numAdminKeys;
|
||||
saveToDisk(SEGMENT_CONFIG);
|
||||
_saveToDisk(SEGMENT_CONFIG);
|
||||
}
|
||||
|
||||
state = loadProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, sizeof(meshtastic_LocalModuleConfig),
|
||||
@@ -1363,7 +1335,7 @@ void NodeDB::loadFromDisk()
|
||||
if (moduleConfig.paxcounter.paxcounter_update_interval == 900)
|
||||
moduleConfig.paxcounter.paxcounter_update_interval = 0;
|
||||
|
||||
saveToDisk(SEGMENT_MODULECONFIG);
|
||||
_saveToDisk(SEGMENT_MODULECONFIG);
|
||||
}
|
||||
#if ARCH_PORTDUINO
|
||||
// set any config overrides
|
||||
@@ -1374,34 +1346,6 @@ void NodeDB::loadFromDisk()
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Save a protobuf from a file, return true for success */
|
||||
bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct,
|
||||
bool fullAtomic)
|
||||
{
|
||||
bool okay = false;
|
||||
#ifdef FSCom
|
||||
auto f = SafeFile(filename, fullAtomic);
|
||||
|
||||
LOG_INFO("Save %s", filename);
|
||||
pb_ostream_t stream = {&writecb, static_cast<Print *>(&f), protoSize};
|
||||
|
||||
if (!pb_encode(&stream, fields, dest_struct)) {
|
||||
LOG_ERROR("Error: can't encode protobuf %s", PB_GET_ERROR(&stream));
|
||||
} else {
|
||||
okay = true;
|
||||
}
|
||||
|
||||
bool writeSucceeded = f.close();
|
||||
|
||||
if (!okay || !writeSucceeded) {
|
||||
LOG_ERROR("Can't write prefs!");
|
||||
}
|
||||
#else
|
||||
LOG_ERROR("ERROR: Filesystem not implemented");
|
||||
#endif
|
||||
return okay;
|
||||
}
|
||||
|
||||
bool NodeDB::saveChannelsToDisk()
|
||||
{
|
||||
#ifdef FSCom
|
||||
@@ -1490,7 +1434,7 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat)
|
||||
return success;
|
||||
}
|
||||
|
||||
bool NodeDB::saveToDisk(int saveWhat)
|
||||
bool NodeDB::_saveToDisk(int saveWhat)
|
||||
{
|
||||
LOG_DEBUG("Save to disk %d", saveWhat);
|
||||
bool success = saveToDiskNoRetry(saveWhat);
|
||||
@@ -1514,10 +1458,12 @@ bool NodeDB::saveToDisk(int saveWhat)
|
||||
|
||||
const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex)
|
||||
{
|
||||
FUNCTION_START("readNextMeshNode");
|
||||
meshtastic_NodeInfoLite *retVal = nullptr;
|
||||
if (readIndex < numMeshNodes)
|
||||
return &meshNodes->at(readIndex++);
|
||||
else
|
||||
return NULL;
|
||||
retVal = &meshNodes->at(readIndex++);
|
||||
FUNCTION_END;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
|
||||
@@ -1545,7 +1491,7 @@ uint32_t sinceReceived(const meshtastic_MeshPacket *p)
|
||||
|
||||
#define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline
|
||||
|
||||
size_t NodeDB::getNumOnlineMeshNodes(bool localOnly)
|
||||
size_t NodeDB::_getNumOnlineMeshNodes(bool localOnly)
|
||||
{
|
||||
size_t numseen = 0;
|
||||
|
||||
@@ -1567,8 +1513,10 @@ size_t NodeDB::getNumOnlineMeshNodes(bool localOnly)
|
||||
*/
|
||||
void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSource src)
|
||||
{
|
||||
FUNCTION_START("updatePosition");
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
||||
if (!info) {
|
||||
FUNCTION_END;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1577,7 +1525,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
LOG_INFO("updatePosition LOCAL pos@%x time=%u lat=%d lon=%d alt=%d", p.timestamp, p.time, p.latitude_i, p.longitude_i,
|
||||
p.altitude);
|
||||
|
||||
setLocalPosition(p);
|
||||
_setLocalPosition(p);
|
||||
info->position = TypeConversions::ConvertToPositionLite(p);
|
||||
} else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.timestamp && !p.location_source) {
|
||||
// FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO
|
||||
@@ -1604,7 +1552,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
}
|
||||
info->has_position = true;
|
||||
updateGUIforNode = info;
|
||||
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
_notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
/** Update telemetry info for this node based on received metrics
|
||||
@@ -1612,9 +1561,11 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
*/
|
||||
void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxSource src)
|
||||
{
|
||||
FUNCTION_START("updatePosition");
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
||||
// Environment metrics should never go to NodeDb but we'll safegaurd anyway
|
||||
if (!info || t.which_variant != meshtastic_Telemetry_device_metrics_tag) {
|
||||
FUNCTION_END;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1627,7 +1578,8 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS
|
||||
info->device_metrics = t.variant.device_metrics;
|
||||
info->has_device_metrics = true;
|
||||
updateGUIforNode = info;
|
||||
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
_notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1635,8 +1587,10 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS
|
||||
*/
|
||||
void NodeDB::addFromContact(meshtastic_SharedContact contact)
|
||||
{
|
||||
FUNCTION_START("addFromContact");
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(contact.node_num);
|
||||
if (!info || !contact.has_user) {
|
||||
FUNCTION_END;
|
||||
return;
|
||||
}
|
||||
// If the local node has this node marked as manually verified
|
||||
@@ -1645,6 +1599,7 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact)
|
||||
if ((info->bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK) && !contact.manually_verified) {
|
||||
if (contact.user.public_key.size != info->user.public_key.size ||
|
||||
memcmp(contact.user.public_key.bytes, info->user.public_key.bytes, info->user.public_key.size) != 0) {
|
||||
FUNCTION_END;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1688,22 +1643,26 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact)
|
||||
// Mark the node's key as manually verified to indicate trustworthiness.
|
||||
updateGUIforNode = info;
|
||||
sortMeshDB();
|
||||
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
_notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
}
|
||||
saveNodeDatabaseToDisk();
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
/** Update user info and channel for this node based on received user data
|
||||
*/
|
||||
bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex)
|
||||
{
|
||||
FUNCTION_START("updateUser");
|
||||
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
||||
if (!info) {
|
||||
FUNCTION_END;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
if (p.public_key.size == 32 && nodeId != nodeDB->getNodeNum()) {
|
||||
if (p.public_key.size == 32 && nodeId != getNodeNum()) {
|
||||
printBytes("Incoming Pubkey: ", p.public_key.bytes, 32);
|
||||
|
||||
// Alert the user if a remote node is advertising public key that matches our own
|
||||
@@ -1720,6 +1679,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
|
||||
sprintf(cn->message, warning, p.long_name);
|
||||
service->sendClientNotification(cn);
|
||||
}
|
||||
FUNCTION_END;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1727,6 +1687,7 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
|
||||
// if the key doesn't match, don't update nodeDB at all.
|
||||
if (p.public_key.size != 32 || (memcmp(p.public_key.bytes, info->user.public_key.bytes, 32) != 0)) {
|
||||
LOG_WARN("Public Key mismatch, dropping NodeInfo");
|
||||
FUNCTION_END;
|
||||
return false;
|
||||
}
|
||||
LOG_INFO("Public Key set for node, not updating!");
|
||||
@@ -1754,19 +1715,19 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
|
||||
|
||||
if (changed) {
|
||||
updateGUIforNode = info;
|
||||
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
_notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
|
||||
// We just changed something about a User,
|
||||
// store our DB unless we just did so less than a minute ago
|
||||
|
||||
if (!Throttle::isWithinTimespanMs(lastNodeDbSave, ONE_MINUTE_MS)) {
|
||||
saveToDisk(SEGMENT_NODEDATABASE);
|
||||
_saveToDisk(SEGMENT_NODEDATABASE);
|
||||
lastNodeDbSave = millis();
|
||||
} else {
|
||||
LOG_DEBUG("Defer NodeDB saveToDisk for now");
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION_END;
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -1774,107 +1735,121 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
|
||||
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
|
||||
void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
FUNCTION_START("updateFrom");
|
||||
if (mp.from == getNodeNum()) {
|
||||
LOG_DEBUG("Ignore update from self");
|
||||
return;
|
||||
}
|
||||
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
|
||||
} else if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
|
||||
LOG_DEBUG("Update DB node 0x%x, rx_time=%u", mp.from, mp.rx_time);
|
||||
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getFrom(&mp));
|
||||
if (!info) {
|
||||
return;
|
||||
if (info) {
|
||||
if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard
|
||||
info->last_heard = mp.rx_time;
|
||||
|
||||
if (mp.rx_snr)
|
||||
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
|
||||
|
||||
info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT
|
||||
|
||||
// If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway
|
||||
if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) {
|
||||
info->has_hops_away = true;
|
||||
info->hops_away = mp.hop_start - mp.hop_limit;
|
||||
}
|
||||
sortMeshDB();
|
||||
}
|
||||
|
||||
if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard
|
||||
info->last_heard = mp.rx_time;
|
||||
|
||||
if (mp.rx_snr)
|
||||
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
|
||||
|
||||
info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT
|
||||
|
||||
// If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway
|
||||
if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) {
|
||||
info->has_hops_away = true;
|
||||
info->hops_away = mp.hop_start - mp.hop_limit;
|
||||
}
|
||||
sortMeshDB();
|
||||
}
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
void NodeDB::set_favorite(bool is_favorite, uint32_t nodeId)
|
||||
{
|
||||
meshtastic_NodeInfoLite *lite = getMeshNode(nodeId);
|
||||
FUNCTION_START("set_favorite");
|
||||
meshtastic_NodeInfoLite *lite = _getMeshNode(nodeId);
|
||||
if (lite && lite->is_favorite != is_favorite) {
|
||||
lite->is_favorite = is_favorite;
|
||||
sortMeshDB();
|
||||
saveNodeDatabaseToDisk();
|
||||
}
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
// returns true if nodeId is_favorite; false if not or not found
|
||||
bool NodeDB::isFavorite(uint32_t nodeId)
|
||||
{
|
||||
// returns true if nodeId is_favorite; false if not or not found
|
||||
|
||||
FUNCTION_START("set_favorite");
|
||||
// NODENUM_BROADCAST will never be in the DB
|
||||
if (nodeId == NODENUM_BROADCAST)
|
||||
if (nodeId == NODENUM_BROADCAST) {
|
||||
FUNCTION_END;
|
||||
return false;
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite *lite = getMeshNode(nodeId);
|
||||
meshtastic_NodeInfoLite *lite = _getMeshNode(nodeId);
|
||||
|
||||
if (lite) {
|
||||
FUNCTION_END;
|
||||
return lite->is_favorite;
|
||||
}
|
||||
FUNCTION_END;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NodeDB::isFromOrToFavoritedNode(const meshtastic_MeshPacket &p)
|
||||
{
|
||||
FUNCTION_START("isFromOrToFavoritedNode");
|
||||
// This method is logically equivalent to:
|
||||
// return isFavorite(p.from) || isFavorite(p.to);
|
||||
// but is more efficient by:
|
||||
// 1. doing only one pass through the database, instead of two
|
||||
// 2. exiting early when a favorite is found, or if both from and to have been seen
|
||||
|
||||
if (p.to == NODENUM_BROADCAST)
|
||||
return isFavorite(p.from); // we never store NODENUM_BROADCAST in the DB, so we only need to check p.from
|
||||
|
||||
meshtastic_NodeInfoLite *lite = NULL;
|
||||
|
||||
bool seenFrom = false;
|
||||
bool seenTo = false;
|
||||
|
||||
if (p.to == NODENUM_BROADCAST)
|
||||
seenTo = true;
|
||||
|
||||
for (int i = 0; i < numMeshNodes; i++) {
|
||||
lite = &meshNodes->at(i);
|
||||
|
||||
if (lite->num == p.from) {
|
||||
if (lite->is_favorite)
|
||||
if (!seenFrom && lite->num == p.from) {
|
||||
if (lite->is_favorite) {
|
||||
FUNCTION_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
seenFrom = true;
|
||||
}
|
||||
|
||||
if (lite->num == p.to) {
|
||||
if (lite->is_favorite)
|
||||
if (!seenTo && lite->num == p.to) {
|
||||
if (lite->is_favorite) {
|
||||
FUNCTION_END;
|
||||
return true;
|
||||
}
|
||||
|
||||
seenTo = true;
|
||||
}
|
||||
|
||||
if (seenFrom && seenTo)
|
||||
if (seenFrom && seenTo) {
|
||||
FUNCTION_END;
|
||||
return false; // we've seen both, and neither is a favorite, so we can stop searching early
|
||||
}
|
||||
|
||||
// Note: if we knew that sortMeshDB was always called after any change to is_favorite, we could exit early after searching
|
||||
// all favorited nodes first.
|
||||
}
|
||||
|
||||
FUNCTION_END;
|
||||
return false;
|
||||
}
|
||||
|
||||
void NodeDB::pause_sort(bool paused)
|
||||
{
|
||||
// Including the mutex macro for completeness, but it's possible it isn't appropriate here
|
||||
FUNCTION_START("pause_sort");
|
||||
sortingIsPaused = paused;
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
void NodeDB::sortMeshDB()
|
||||
@@ -1909,10 +1884,13 @@ void NodeDB::sortMeshDB()
|
||||
|
||||
uint8_t NodeDB::getMeshNodeChannel(NodeNum n)
|
||||
{
|
||||
const meshtastic_NodeInfoLite *info = getMeshNode(n);
|
||||
FUNCTION_START("getMeshNodeChannel");
|
||||
const meshtastic_NodeInfoLite *info = _getMeshNode(n);
|
||||
if (!info) {
|
||||
FUNCTION_END;
|
||||
return 0; // defaults to PRIMARY
|
||||
}
|
||||
FUNCTION_END;
|
||||
return info->channel;
|
||||
}
|
||||
|
||||
@@ -1925,7 +1903,7 @@ std::string NodeDB::getNodeId() const
|
||||
|
||||
/// Find a node in our DB, return null for missing
|
||||
/// NOTE: This function might be called from an ISR
|
||||
meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n)
|
||||
meshtastic_NodeInfoLite *NodeDB::_getMeshNode(NodeNum n)
|
||||
{
|
||||
for (int i = 0; i < numMeshNodes; i++)
|
||||
if (meshNodes->at(i).num == n)
|
||||
@@ -1935,7 +1913,7 @@ meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n)
|
||||
}
|
||||
|
||||
// returns true if the maximum number of nodes is reached or we are running low on memory
|
||||
bool NodeDB::isFull()
|
||||
bool NodeDB::_isFull()
|
||||
{
|
||||
return (numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < MINIMUM_SAFE_FREE_HEAP);
|
||||
}
|
||||
@@ -1943,7 +1921,7 @@ bool NodeDB::isFull()
|
||||
/// Find a node in our DB, create an empty NodeInfo if missing
|
||||
meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
|
||||
{
|
||||
meshtastic_NodeInfoLite *lite = getMeshNode(n);
|
||||
meshtastic_NodeInfoLite *lite = _getMeshNode(n);
|
||||
|
||||
if (!lite) {
|
||||
if (isFull()) {
|
||||
@@ -1998,18 +1976,25 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
|
||||
/// valid lat/lon
|
||||
bool NodeDB::hasValidPosition(const meshtastic_NodeInfoLite *n)
|
||||
{
|
||||
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
|
||||
FUNCTION_START("hasValidPosition");
|
||||
auto retVal = n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
|
||||
FUNCTION_END;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// If we have a node / user and they report is_licensed = true
|
||||
/// we consider them licensed
|
||||
UserLicenseStatus NodeDB::getLicenseStatus(uint32_t nodeNum)
|
||||
{
|
||||
meshtastic_NodeInfoLite *info = getMeshNode(nodeNum);
|
||||
FUNCTION_START("getLicenseStatus");
|
||||
meshtastic_NodeInfoLite *info = _getMeshNode(nodeNum);
|
||||
if (!info || !info->has_user) {
|
||||
FUNCTION_END;
|
||||
return UserLicenseStatus::NotKnown;
|
||||
}
|
||||
return info->user.is_licensed ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed;
|
||||
auto retVal = info->user.is_licensed ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed;
|
||||
FUNCTION_END;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
#if !defined(MESHTASTIC_EXCLUDE_PKI)
|
||||
@@ -2031,6 +2016,7 @@ bool NodeDB::checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_pub
|
||||
|
||||
bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
|
||||
{
|
||||
FUNCTION_START("backupPreferences");
|
||||
bool success = false;
|
||||
lastBackupAttempt = millis();
|
||||
#ifdef FSCom
|
||||
@@ -2064,11 +2050,13 @@ bool NodeDB::backupPreferences(meshtastic_AdminMessage_BackupLocation location)
|
||||
// TODO: After more mainline SD card support
|
||||
}
|
||||
#endif
|
||||
FUNCTION_END;
|
||||
return success;
|
||||
}
|
||||
|
||||
bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location, int restoreWhat)
|
||||
{
|
||||
FUNCTION_START("backupPreferences");
|
||||
bool success = false;
|
||||
#ifdef FSCom
|
||||
if (location == meshtastic_AdminMessage_BackupLocation_FLASH) {
|
||||
@@ -2076,6 +2064,7 @@ bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location,
|
||||
if (!FSCom.exists(backupFileName)) {
|
||||
spiLock->unlock();
|
||||
LOG_WARN("Could not restore. No backup file found");
|
||||
FUNCTION_END;
|
||||
return false;
|
||||
} else {
|
||||
spiLock->unlock();
|
||||
@@ -2101,7 +2090,7 @@ bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location,
|
||||
LOG_DEBUG("Restored channels");
|
||||
}
|
||||
|
||||
success = saveToDisk(restoreWhat);
|
||||
success = _saveToDisk(restoreWhat);
|
||||
if (success) {
|
||||
LOG_INFO("Restored preferences from backup");
|
||||
} else {
|
||||
@@ -2113,6 +2102,7 @@ bool NodeDB::restorePreferences(meshtastic_AdminMessage_BackupLocation location,
|
||||
} else if (location == meshtastic_AdminMessage_BackupLocation_SD) {
|
||||
// TODO: After more mainline SD card support
|
||||
}
|
||||
FUNCTION_END;
|
||||
return success;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -18,6 +18,13 @@
|
||||
#include "PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
#define FUNCTION_START(FUNCTION_NAME) \
|
||||
if (fakeMutex) \
|
||||
LOG_ERROR("Concurrency violation in " FUNCTION_NAME); \
|
||||
fakeMutex = true;
|
||||
|
||||
#define FUNCTION_END fakeMutex = false;
|
||||
|
||||
#if !defined(MESHTASTIC_EXCLUDE_PKI)
|
||||
// E3B0C442 is the blank hash
|
||||
static const uint8_t LOW_ENTROPY_HASHES[][32] = {
|
||||
@@ -110,19 +117,6 @@ uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n);
|
||||
/// Given a packet, return how many seconds in the past (vs now) it was received
|
||||
uint32_t sinceReceived(const meshtastic_MeshPacket *p);
|
||||
|
||||
enum LoadFileResult {
|
||||
// Successfully opened the file
|
||||
LOAD_SUCCESS = 1,
|
||||
// File does not exist
|
||||
NOT_FOUND = 2,
|
||||
// Device does not have a filesystem
|
||||
NO_FILESYSTEM = 3,
|
||||
// File exists, but could not decode protobufs
|
||||
DECODE_FAILED = 4,
|
||||
// File exists, but open failed for some reason
|
||||
OTHER_FAILURE = 5
|
||||
};
|
||||
|
||||
enum UserLicenseStatus { NotKnown, NotLicensed, Licensed };
|
||||
|
||||
class NodeDB
|
||||
@@ -135,7 +129,6 @@ class NodeDB
|
||||
// Note: these two references just point into our static array we serialize to/from disk
|
||||
|
||||
public:
|
||||
std::vector<meshtastic_NodeInfoLite> *meshNodes;
|
||||
bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled
|
||||
meshtastic_NodeInfoLite *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI
|
||||
Observable<const meshtastic::NodeStatus *> newStatus;
|
||||
@@ -151,17 +144,26 @@ class NodeDB
|
||||
/// write to flash
|
||||
/// @return true if the save was successful
|
||||
bool saveToDisk(int saveWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS |
|
||||
SEGMENT_NODEDATABASE);
|
||||
SEGMENT_NODEDATABASE)
|
||||
{
|
||||
FUNCTION_START("saveToDisk");
|
||||
auto retVal = _saveToDisk(saveWhat);
|
||||
FUNCTION_END;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/** Reinit radio config if needed, because either:
|
||||
* a) sometimes a buggy android app might send us bogus settings or
|
||||
* b) the client set factory_reset
|
||||
*
|
||||
* @param factory_reset if true, reset all settings to factory defaults
|
||||
* @param is_fresh_install set to true after a fresh install, to trigger NodeInfo/Position requests
|
||||
* @return true if the config was completely reset, in that case, we should send it back to the client
|
||||
*/
|
||||
void resetRadioConfig(bool is_fresh_install = false);
|
||||
void resetRadioConfig(bool is_fresh_install = false)
|
||||
{
|
||||
FUNCTION_START("resetRadioConfig");
|
||||
_resetRadioConfig(is_fresh_install);
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
/// given a subpacket sniffed from the network, update our DB state
|
||||
/// we updateGUI and updateGUIforNode if we think our this change is big enough for a redraw
|
||||
@@ -208,7 +210,13 @@ class NodeDB
|
||||
std::string getNodeId() const;
|
||||
|
||||
// @return last byte of a NodeNum, 0xFF if it ended at 0x00
|
||||
uint8_t getLastByteOfNodeNum(NodeNum num) { return (uint8_t)((num & 0xFF) ? (num & 0xFF) : 0xFF); }
|
||||
uint8_t getLastByteOfNodeNum(NodeNum num)
|
||||
{
|
||||
FUNCTION_START("getLastByteOfNodeNum");
|
||||
auto retVal = (uint8_t)((num & 0xFF) ? (num & 0xFF) : 0xFF);
|
||||
FUNCTION_END;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// if returns false, that means our node should send a DenyNodeNum response. If true, we think the number is okay for use
|
||||
// bool handleWantNodeNum(NodeNum n);
|
||||
@@ -227,79 +235,104 @@ class NodeDB
|
||||
/* Return the number of nodes we've heard from recently (within the last 2 hrs?)
|
||||
* @param localOnly if true, ignore nodes heard via MQTT
|
||||
*/
|
||||
size_t getNumOnlineMeshNodes(bool localOnly = false);
|
||||
size_t getNumOnlineMeshNodes(bool localOnly = false)
|
||||
{
|
||||
FUNCTION_START("getNumOnlineMeshNodes");
|
||||
auto retVal = _getNumOnlineMeshNodes(localOnly);
|
||||
FUNCTION_END;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void initConfigIntervals(), initModuleConfigIntervals(), resetNodes(bool keepFavorites = false),
|
||||
removeNodeByNum(NodeNum nodeNum);
|
||||
void resetNodes(bool keepFavorites = false), removeNodeByNum(NodeNum nodeNum);
|
||||
|
||||
bool factoryReset(bool eraseBleBonds = false);
|
||||
|
||||
LoadFileResult loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields,
|
||||
void *dest_struct);
|
||||
bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct,
|
||||
bool fullAtomic = true);
|
||||
|
||||
void installRoleDefaults(meshtastic_Config_DeviceConfig_Role role);
|
||||
void installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
|
||||
{
|
||||
FUNCTION_START("installRoleDefaults");
|
||||
_installRoleDefaults(role);
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
const meshtastic_NodeInfoLite *readNextMeshNode(uint32_t &readIndex);
|
||||
|
||||
meshtastic_NodeInfoLite *getMeshNodeByIndex(size_t x)
|
||||
{
|
||||
assert(x < numMeshNodes);
|
||||
return &meshNodes->at(x);
|
||||
FUNCTION_START("getMeshNodeByIndex");
|
||||
meshtastic_NodeInfoLite *retValue = nullptr;
|
||||
if (x < numMeshNodes)
|
||||
retValue = &meshNodes->at(x);
|
||||
FUNCTION_END;
|
||||
return retValue;
|
||||
}
|
||||
|
||||
virtual meshtastic_NodeInfoLite *getMeshNode(NodeNum n);
|
||||
size_t getNumMeshNodes() { return numMeshNodes; }
|
||||
virtual meshtastic_NodeInfoLite *getMeshNode(NodeNum n)
|
||||
{
|
||||
FUNCTION_START("getMeshNode");
|
||||
auto retVal = _getMeshNode(n);
|
||||
FUNCTION_END;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
size_t getNumMeshNodes()
|
||||
{
|
||||
FUNCTION_START("getNumMeshNodes");
|
||||
auto retVal = numMeshNodes;
|
||||
FUNCTION_END;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
UserLicenseStatus getLicenseStatus(uint32_t nodeNum);
|
||||
|
||||
size_t getMaxNodesAllocatedSize()
|
||||
// returns true if the maximum number of nodes is reached or we are running low on memory
|
||||
bool isFull()
|
||||
{
|
||||
meshtastic_NodeDatabase emptyNodeDatabase;
|
||||
emptyNodeDatabase.version = DEVICESTATE_CUR_VER;
|
||||
size_t nodeDatabaseSize;
|
||||
pb_get_encoded_size(&nodeDatabaseSize, meshtastic_NodeDatabase_fields, &emptyNodeDatabase);
|
||||
return nodeDatabaseSize + (MAX_NUM_NODES * meshtastic_NodeInfoLite_size);
|
||||
FUNCTION_START("isFull");
|
||||
auto retVal = _isFull();
|
||||
FUNCTION_END;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
// returns true if the maximum number of nodes is reached or we are running low on memory
|
||||
bool isFull();
|
||||
|
||||
void clearLocalPosition();
|
||||
void clearLocalPosition()
|
||||
{
|
||||
FUNCTION_START("clearLocalPosition");
|
||||
_clearLocalPosition();
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
void setLocalPosition(meshtastic_Position position, bool timeOnly = false)
|
||||
{
|
||||
if (timeOnly) {
|
||||
LOG_DEBUG("Set local position time only: time=%u timestamp=%u", position.time, position.timestamp);
|
||||
localPosition.time = position.time;
|
||||
localPosition.timestamp = position.timestamp > 0 ? position.timestamp : position.time;
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Set local position: lat=%i lon=%i time=%u timestamp=%u", position.latitude_i, position.longitude_i,
|
||||
position.time, position.timestamp);
|
||||
localPosition = position;
|
||||
FUNCTION_START("setLocalPosition");
|
||||
_setLocalPosition(position, timeOnly);
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
bool hasValidPosition(const meshtastic_NodeInfoLite *n);
|
||||
|
||||
#if !defined(MESHTASTIC_EXCLUDE_PKI)
|
||||
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
||||
#endif
|
||||
|
||||
bool backupPreferences(meshtastic_AdminMessage_BackupLocation location);
|
||||
bool restorePreferences(meshtastic_AdminMessage_BackupLocation location,
|
||||
int restoreWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS);
|
||||
|
||||
/// Notify observers of changes to the DB
|
||||
void notifyObservers(bool forceUpdate = false)
|
||||
{
|
||||
// Notify observers of the current node state
|
||||
const meshtastic::NodeStatus status = meshtastic::NodeStatus(getNumOnlineMeshNodes(), getNumMeshNodes(), forceUpdate);
|
||||
newStatus.notifyObservers(&status);
|
||||
FUNCTION_START("notifyObservers");
|
||||
_notifyObservers(forceUpdate);
|
||||
FUNCTION_END;
|
||||
}
|
||||
|
||||
private:
|
||||
bool fakeMutex = false;
|
||||
|
||||
/// Notify observers of changes to the DB
|
||||
void _notifyObservers(bool forceUpdate = false)
|
||||
{
|
||||
// Notify observers of the current node state
|
||||
const meshtastic::NodeStatus status = meshtastic::NodeStatus(_getNumOnlineMeshNodes(), numMeshNodes, forceUpdate);
|
||||
newStatus.notifyObservers(&status);
|
||||
}
|
||||
|
||||
std::vector<meshtastic_NodeInfoLite> *meshNodes;
|
||||
|
||||
bool duplicateWarned = false;
|
||||
uint32_t lastNodeDbSave = 0; // when we last saved our db to flash
|
||||
uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually
|
||||
@@ -333,6 +366,51 @@ class NodeDB
|
||||
bool saveDeviceStateToDisk();
|
||||
bool saveNodeDatabaseToDisk();
|
||||
void sortMeshDB();
|
||||
|
||||
void initConfigIntervals(), initModuleConfigIntervals();
|
||||
|
||||
size_t getMaxNodesAllocatedSize()
|
||||
{
|
||||
meshtastic_NodeDatabase emptyNodeDatabase;
|
||||
emptyNodeDatabase.version = DEVICESTATE_CUR_VER;
|
||||
size_t nodeDatabaseSize;
|
||||
pb_get_encoded_size(&nodeDatabaseSize, meshtastic_NodeDatabase_fields, &emptyNodeDatabase);
|
||||
return nodeDatabaseSize + (MAX_NUM_NODES * meshtastic_NodeInfoLite_size);
|
||||
}
|
||||
|
||||
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
||||
|
||||
// wrapped private functions:
|
||||
|
||||
bool _saveToDisk(int saveWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS |
|
||||
SEGMENT_NODEDATABASE);
|
||||
void _resetRadioConfig(bool is_fresh_install = false);
|
||||
|
||||
/* Return the number of nodes we've heard from recently (within the last 2 hrs?)
|
||||
* @param localOnly if true, ignore nodes heard via MQTT
|
||||
*/
|
||||
size_t _getNumOnlineMeshNodes(bool localOnly = false);
|
||||
|
||||
void _installRoleDefaults(meshtastic_Config_DeviceConfig_Role role);
|
||||
|
||||
meshtastic_NodeInfoLite *_getMeshNode(NodeNum n);
|
||||
|
||||
bool _isFull();
|
||||
|
||||
void _clearLocalPosition();
|
||||
|
||||
void _setLocalPosition(meshtastic_Position position, bool timeOnly = false)
|
||||
{
|
||||
if (timeOnly) {
|
||||
LOG_DEBUG("Set local position time only: time=%u timestamp=%u", position.time, position.timestamp);
|
||||
localPosition.time = position.time;
|
||||
localPosition.timestamp = position.timestamp > 0 ? position.timestamp : position.time;
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Set local position: lat=%i lon=%i time=%u timestamp=%u", position.latitude_i, position.longitude_i,
|
||||
position.time, position.timestamp);
|
||||
localPosition = position;
|
||||
}
|
||||
};
|
||||
|
||||
extern NodeDB *nodeDB;
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
|
||||
static MemoryDynamic<meshtastic_MeshPacket> dynamicPool;
|
||||
Allocator<meshtastic_MeshPacket> &packetPool = dynamicPool;
|
||||
#elif defined(ARCH_STM32WL)
|
||||
// On STM32 there isn't enough heap left over for the rest of the firmware if we allocate this statically.
|
||||
#elif defined(ARCH_STM32WL) || defined(BOARD_HAS_PSRAM)
|
||||
// On STM32 and boards with PSRAM, there isn't enough heap left over for the rest of the firmware if we allocate this statically.
|
||||
// For now, make it dynamic again.
|
||||
#define MAX_PACKETS \
|
||||
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \
|
||||
|
||||
@@ -237,8 +237,8 @@ typedef enum _meshtastic_HardwareModel {
|
||||
meshtastic_HardwareModel_T_ETH_ELITE = 91,
|
||||
/* Heltec HRI-3621 industrial probe */
|
||||
meshtastic_HardwareModel_HELTEC_SENSOR_HUB = 92,
|
||||
/* Reserved Fried Chicken ID for future use */
|
||||
meshtastic_HardwareModel_RESERVED_FRIED_CHICKEN = 93,
|
||||
/* Muzi Works Muzi-Base device */
|
||||
meshtastic_HardwareModel_MUZI_BASE = 93,
|
||||
/* Heltec Magnetic Power Bank with Meshtastic compatible */
|
||||
meshtastic_HardwareModel_HELTEC_MESH_POCKET = 94,
|
||||
/* Seeed Solar Node */
|
||||
|
||||
@@ -1322,7 +1322,7 @@ void AdminModule::saveChanges(int saveWhat, bool shouldReboot)
|
||||
|
||||
void AdminModule::handleStoreDeviceUIConfig(const meshtastic_DeviceUIConfig &uicfg)
|
||||
{
|
||||
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uicfg);
|
||||
saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uicfg);
|
||||
}
|
||||
|
||||
void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p)
|
||||
|
||||
@@ -2273,9 +2273,9 @@ ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &
|
||||
|
||||
void CannedMessageModule::loadProtoForModule()
|
||||
{
|
||||
if (nodeDB->loadProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size,
|
||||
sizeof(meshtastic_CannedMessageModuleConfig), &meshtastic_CannedMessageModuleConfig_msg,
|
||||
&cannedMessageModuleConfig) != LoadFileResult::LOAD_SUCCESS) {
|
||||
if (loadProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size,
|
||||
sizeof(meshtastic_CannedMessageModuleConfig), &meshtastic_CannedMessageModuleConfig_msg,
|
||||
&cannedMessageModuleConfig) != LoadFileResult::LOAD_SUCCESS) {
|
||||
installDefaultCannedMessageModuleConfig();
|
||||
}
|
||||
}
|
||||
@@ -2295,8 +2295,8 @@ bool CannedMessageModule::saveProtoForModule()
|
||||
spiLock->unlock();
|
||||
#endif
|
||||
|
||||
okay &= nodeDB->saveProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size,
|
||||
&meshtastic_CannedMessageModuleConfig_msg, &cannedMessageModuleConfig);
|
||||
okay &= saveProto(cannedMessagesConfigFile, meshtastic_CannedMessageModuleConfig_size,
|
||||
&meshtastic_CannedMessageModuleConfig_msg, &cannedMessageModuleConfig);
|
||||
|
||||
return okay;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* @date [Insert Date]
|
||||
*/
|
||||
#include "ExternalNotificationModule.h"
|
||||
#include "FSCommon.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
@@ -370,8 +371,8 @@ ExternalNotificationModule::ExternalNotificationModule()
|
||||
if (inputBroker) // put our callback in the inputObserver list
|
||||
inputObserver.observe(inputBroker);
|
||||
#endif
|
||||
if (nodeDB->loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig),
|
||||
&meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) {
|
||||
if (loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig), &meshtastic_RTTTLConfig_msg,
|
||||
&rtttlConfig) != LoadFileResult::LOAD_SUCCESS) {
|
||||
memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone));
|
||||
// The default ringtone is always loaded from userPrefs.jsonc
|
||||
strncpy(rtttlConfig.ringtone, USERPREFS_RINGTONE_RTTTL, sizeof(rtttlConfig.ringtone));
|
||||
@@ -640,7 +641,7 @@ void ExternalNotificationModule::handleSetRingtone(const char *from_msg)
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
nodeDB->saveProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, &meshtastic_RTTTLConfig_msg, &rtttlConfig);
|
||||
saveProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, &meshtastic_RTTTLConfig_msg, &rtttlConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,16 +64,6 @@ void onConnect(uint16_t conn_handle)
|
||||
connection->getPeerName(central_name, sizeof(central_name));
|
||||
LOG_INFO("BLE Connected to %s", central_name);
|
||||
|
||||
// negotiate connections params as soon as possible
|
||||
|
||||
ble_gap_conn_params_t newParams;
|
||||
newParams.min_conn_interval = 24;
|
||||
newParams.max_conn_interval = 40;
|
||||
newParams.slave_latency = 5;
|
||||
newParams.conn_sup_timeout = 400;
|
||||
|
||||
sd_ble_gap_conn_param_update(conn_handle, &newParams);
|
||||
|
||||
// Notify UI (or any other interested firmware components)
|
||||
meshtastic::BluetoothStatus newStatus(meshtastic::BluetoothStatus::ConnectionState::CONNECTED);
|
||||
bluetoothStatus->updateStatus(&newStatus);
|
||||
@@ -129,7 +119,7 @@ void startAdv(void)
|
||||
Bluefruit.Advertising.addService(meshBleService);
|
||||
/* Start Advertising
|
||||
* - Enable auto advertising if disconnected
|
||||
* - Interval: fast mode = 20 ms, slow mode = 417,5 ms
|
||||
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
|
||||
* - Timeout for fast mode is 30 seconds
|
||||
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
|
||||
*
|
||||
@@ -137,7 +127,7 @@ void startAdv(void)
|
||||
* https://developer.apple.com/library/content/qa/qa1931/_index.html
|
||||
*/
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 668); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds. FIXME, we should stop advertising after X
|
||||
}
|
||||
@@ -282,24 +272,6 @@ void NRF52Bluetooth::setup()
|
||||
// Set the connect/disconnect callback handlers
|
||||
Bluefruit.Periph.setConnectCallback(onConnect);
|
||||
Bluefruit.Periph.setDisconnectCallback(onDisconnect);
|
||||
|
||||
// Set slave latency to 5 to conserve power
|
||||
// Despite name this does not impact data transfer
|
||||
// https://docs.silabs.com/bluetooth/2.13/bluetooth-general-system-and-performance/optimizing-current-consumption-in-bluetooth-low-energy-devices
|
||||
|
||||
Bluefruit.Periph.setConnSlaveLatency(5);
|
||||
|
||||
// TODO: Adafruit defaul min, max interval seems to be (20,30) [in 1.25 ms units] -> (25.00, 31.25) milliseconds
|
||||
// so using formula Interval Max * (Slave Latency + 1) ≤ 2 seconds
|
||||
// max slave latency we can use is 30 (max available in BLE)
|
||||
// and even double max inteval (see apple doc linked above for formulas)
|
||||
// See Periph.SetConnInterval method
|
||||
|
||||
// Tweak this later for even more power savings once those changes are confirmed to work well.
|
||||
// Changing min, max interval may slow BLE transfer a bit - bumping slave latency will most likely not.
|
||||
|
||||
|
||||
|
||||
#ifndef BLE_DFU_SECURE
|
||||
bledfu.setPermission(SECMODE_ENC_WITH_MITM, SECMODE_ENC_WITH_MITM);
|
||||
bledfu.begin(); // Install the DFU helper
|
||||
@@ -328,7 +300,7 @@ void NRF52Bluetooth::setup()
|
||||
void NRF52Bluetooth::resumeAdvertising()
|
||||
{
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
Bluefruit.Advertising.setInterval(32, 668); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
#elif defined(HELTEC_MESH_SOLAR)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_SOLAR
|
||||
#elif defined(MUZI_BASE)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_RESERVED_FRIED_CHICKEN
|
||||
#define HW_VENDOR meshtastic_HardwareModel_MUZI_BASE
|
||||
#else
|
||||
#define HW_VENDOR meshtastic_HardwareModel_NRF52_UNKNOWN
|
||||
#endif
|
||||
|
||||
@@ -335,6 +335,16 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
if (Serial1) // A straightforward solution to the wake from deepsleep problem
|
||||
Serial1.end();
|
||||
#endif
|
||||
|
||||
#ifdef TTGO_T_ECHO
|
||||
// To power off the T-Echo, the display must be set
|
||||
// as an input pin; otherwise, there will be leakage current.
|
||||
pinMode(PIN_EINK_CS, INPUT);
|
||||
pinMode(PIN_EINK_DC, INPUT);
|
||||
pinMode(PIN_EINK_RES, INPUT);
|
||||
pinMode(PIN_EINK_BUSY, INPUT);
|
||||
#endif
|
||||
|
||||
setBluetoothEnable(false);
|
||||
|
||||
#ifdef RAK4630
|
||||
|
||||
@@ -13,7 +13,10 @@ build_flags =
|
||||
-DEINK_WIDTH=250
|
||||
-DEINK_HEIGHT=122
|
||||
-DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk
|
||||
-DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted
|
||||
-DEINK_LIMIT_FASTREFRESH=20 ; How many consecutive fast-refreshes are permitted //20
|
||||
-DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates //30
|
||||
-DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates
|
||||
-DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached.
|
||||
|
||||
lib_deps =
|
||||
${esp32s3_base.lib_deps}
|
||||
|
||||
@@ -62,17 +62,11 @@ extern "C" {
|
||||
/*
|
||||
* Buttons
|
||||
*/
|
||||
#define PIN_BUTTON2 (32 + 10)
|
||||
#define PIN_BUTTON1 (32 + 10)
|
||||
#define PIN_BUTTON2 (32 + 7)
|
||||
#define ALT_BUTTON_PIN PIN_BUTTON2
|
||||
#define ALT_BUTTON_ACTIVE_LOW true
|
||||
#define ALT_BUTTON_ACTIVE_PULLUP true
|
||||
#define PIN_BUTTON1 (32 + 7)
|
||||
|
||||
// #define PIN_BUTTON1 (0 + 11)
|
||||
// #define PIN_BUTTON1 (32 + 7)
|
||||
|
||||
// #define BUTTON_CLICK_MS 400
|
||||
// #define BUTTON_TOUCH_MS 200
|
||||
|
||||
/*
|
||||
* Analog pins
|
||||
@@ -203,4 +197,4 @@ External serial flash WP25R1635FZUIL0
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
/** Master clock frequency */
|
||||
#define VARIANT_MCK (64000000ul)
|
||||
|
||||
//#define USE_LFXO // Board uses 32khz crystal for LF
|
||||
#define USE_LFRC // Board uses RC for LF
|
||||
#define USE_LFXO // Board uses 32khz crystal for LF
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
@@ -181,9 +181,9 @@ External serial flash WP25R1635FZUIL0
|
||||
|
||||
#define PIN_GPS_STANDBY (32 + 2) // An output to wake GPS, low means allow sleep, high means force wake
|
||||
// Seems to be missing on this new board
|
||||
// #define PIN_GPS_PPS (32 + 4) // Pulse per second input from the GPS
|
||||
#define GPS_TX_PIN (32 + 8) // This is for bits going TOWARDS the CPU
|
||||
#define GPS_RX_PIN (32 + 9) // This is for bits going TOWARDS the GPS
|
||||
#define PIN_GPS_PPS (32 + 4) // Pulse per second input from the GPS
|
||||
#define GPS_TX_PIN (32 + 8) // This is for bits going TOWARDS the CPU
|
||||
#define GPS_RX_PIN (32 + 9) // This is for bits going TOWARDS the GPS
|
||||
|
||||
#define GPS_THREAD_INTERVAL 50
|
||||
|
||||
@@ -203,8 +203,6 @@ External serial flash WP25R1635FZUIL0
|
||||
#define PIN_SPI_MOSI (0 + 22)
|
||||
#define PIN_SPI_SCK (0 + 19)
|
||||
|
||||
#define PIN_PWR_EN (0 + 6)
|
||||
|
||||
// To debug via the segger JLINK console rather than the CDC-ACM serial device
|
||||
// #define USE_SEGGER
|
||||
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
/** Master clock frequency */
|
||||
#define VARIANT_MCK (64000000ul)
|
||||
|
||||
//#define USE_LFXO // Board uses 32khz crystal for LF
|
||||
|
||||
#define USE_LFRC
|
||||
#define USE_LFXO // Board uses 32khz crystal for LF
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
|
||||
Reference in New Issue
Block a user