Compare commits

...

33 Commits

Author SHA1 Message Date
Jonathan Bennett
90e2083d2c clean up merge 2025-12-10 15:27:09 -06:00
Jonathan Bennett
af1d4e08ff Merge branch 'develop' into refactor-nodedb 2025-12-10 11:15:18 -06:00
Jason P
2032ff1c32 Create new screen colors for BaseUI (#8921)
* Create new colors for BaseUI

* Update Ice color
2025-12-10 11:09:37 -06:00
Alex Samorukov
5910cc2e26 Use PSRAM to reduce heap usage percentage on ESP32 with PSRAM (#8891)
* Use PSRAM for malloc > 256bytes to get more heap memory

* Use dynamic allocator on boards with PSRAM to free more heap

* Apply suggestion from @Copilot

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

* Move heap_caps_malloc_extmem_enable() to the top of the init

* Update src/main.cpp

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

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-10 06:23:23 -06:00
Austin
aa72e397f2 PIO: Fix closedcube lib reference (#8920)
Fixes ClosedCube reinstalling on every build
2025-12-09 16:40:37 -06:00
Austin
c55bea8460 ARCtastic (#8904) -- Do It Live!
Actions Runner Controller

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-12-09 15:11:07 -06:00
Austin
aa605fc4a2 Actions: Fix release manifest formating (#8918) 2025-12-09 14:27:13 -06:00
Igor Danilov
d75680a2dd Fix #8915 [Bug]: Exception Decoder does not recognize the backtrace (#8917) 2025-12-09 12:24:41 -06:00
Ben Meadors
decd58cd5c Merge pull request #8913 from meshtastic/revert-8858-nrf52-power-saving-1
Revert "Cut NRF52 bluetooth power usage by 300% - testers needed!"
2025-12-09 08:02:29 -06:00
Ben Meadors
e691bd9732 Revert "Cut NRF52 bluetooth power usage by 300% - testers needed! (#8858)"
This reverts commit ae8d3fbb3d.
2025-12-09 08:02:04 -06:00
Ben Meadors
6bad81f8dd Merge pull request #8911 from vidplace7/fix-chmod
Fix apply device-install permissions
2025-12-09 06:50:19 -06:00
Austin Lane
69b9977fc1 Fix apply device-install permissions
device-install.sh doesn't exist for non-esp32 targets
2025-12-09 07:48:30 -05:00
Ben Meadors
8e63dcf59a Merge branch 'master' into develop 2025-12-09 05:59:15 -06:00
Lewis He
042543eb25 Fixed the issue where T-Echo did not completely shut down peripherals upon power-off. (#8524)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-12-09 05:39:27 -06:00
Austin
928739e0fb Renovate: fix malformed comment for wollewald/BH1750_WE (#8767) 2025-12-08 19:31:28 -06:00
Jonathan Bennett
65c418d4e1 Update protobuf name of FRIED_CHICKEN (#8903) 2025-12-09 11:13:59 +11:00
Jonathan Bennett
c3a69a2742 Fix backwards buttons on Thinknode-M1 (#8901) 2025-12-08 17:58:23 -06:00
Manuel
bd4bcb94f0 tryfix eink parameters (#8898) 2025-12-08 13:14:24 -06:00
github-actions[bot]
eeaafda62a Update protobufs (#8871)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-12-05 10:41:48 -06:00
renovate[bot]
6e9fd189b4 Update meshtastic/device-ui digest to 4fb5f24 (#8862)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-04 15:33:19 -06:00
renovate[bot]
3f40916223 Update alpine Docker tag to v3.23 (#8853)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 15:30:09 -06:00
github-actions[bot]
1b4925bd07 Upgrade trunk (#8849)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-03 07:50:50 -06:00
renovate[bot]
0828c445fb Update actions/stale action to v10.1.1 (#8848)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 05:39:31 -06:00
github-actions[bot]
90584359e4 Upgrade trunk (#8836)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-02 05:48:36 -06:00
Jonathan Bennett
8a43741589 Add 'cleanup' to required PR labels (#8835) 2025-12-02 05:48:19 -06:00
Jonathan Bennett
d772cd4431 Retain getMeshNode virtual marker for tests 2025-10-17 12:16:59 -05:00
Jonathan Bennett
b7b8071056 Merge branch 'develop' into refactor-nodedb 2025-10-17 12:15:36 -05:00
Jonathan Bennett
4a3d28f06b Add FUNCTION_START and FUNCTION_END to public NodeDB functions 2025-10-17 00:36:58 -05:00
Jonathan Bennett
0f4210d2e8 Merge branch 'develop' into refactor-nodedb 2025-10-16 14:40:23 -05:00
Jonathan Bennett
dc6e109329 Finish adding NodeDB wrapper classes 2025-10-16 14:37:33 -05:00
Jonathan Bennett
10141b2562 InkHUD: don't try to access meshNodes directly 2025-10-16 12:08:00 -05:00
Jonathan Bennett
770085ddf7 Begin refactoring nodedb.cpp with public wrappers 2025-10-16 09:44:35 -05:00
Jonathan Bennett
833f950edc Move loadProto and saveProto into FSCommon 2025-10-16 09:36:54 -05:00
33 changed files with 452 additions and 310 deletions

View File

@@ -2,4 +2,5 @@
self-hosted-runner:
# Labels of self-hosted runner in array of strings.
labels:
- arctastic
- test-runner

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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(', ')}.`);

View File

@@ -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.

View File

@@ -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

View File

@@ -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" \

View File

@@ -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":

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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));

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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 + \

View File

@@ -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 */

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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

View File

@@ -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
*----------------------------------------------------------------------------*/

View File

@@ -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

View File

@@ -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