Compare commits

..

25 Commits

Author SHA1 Message Date
Jonathan Bennett
4e30795c6b Check for read-only DB 2025-12-28 14:12:01 -06:00
Jonathan Bennett
0228d4e2df Short hashes in 2025-12-28 13:45:40 -06:00
Jonathan Bennett
11963ea3d2 Screen crash fix 2025-12-28 09:48:32 -06:00
Jonathan Bennett
aeb1e7e71a Minor tweak for behavior when chain is empty 2025-12-27 23:28:00 -06:00
Jonathan Bennett
aecdc93c98 Add more SFPP config values 2025-12-27 21:32:45 -06:00
Jonathan Bennett
fa28412b82 Fix getLinkFromCount() 2025-12-27 21:32:45 -06:00
Jonathan Bennett
cf5ecf30d7 Steal the TEXT_MESSAGE_COMPRESSED portnum 2025-12-27 21:32:45 -06:00
Jonathan Bennett
df3934de85 Misc 2025-12-27 21:32:45 -06:00
Jonathan Bennett
c911a8e3f1 misc partial chain fixes 2025-12-27 21:32:45 -06:00
Jonathan Bennett
f392487b46 protobuf changes to test new SFPP feature 2025-12-27 21:32:45 -06:00
Jonathan Bennett
a8c2fb3945 Add count handling to SFPP 2025-12-27 21:32:45 -06:00
Jonathan Bennett
c0d108adff Don't stash messages without a matching chain root 2025-12-27 21:32:45 -06:00
Jonathan Bennett
f7ccab95b2 Allow UDP and API packets in S$F++ 2025-12-27 21:32:45 -06:00
Jonathan Bennett
dc08311b56 Set hop_limit and hop_start on message rebroadcasts 2025-12-27 21:32:45 -06:00
Jonathan Bennett
ce389e44cd Include hop_start in printPacket 2025-12-27 21:32:45 -06:00
Jonathan Bennett
a19d88b022 Don't wipe scratch 2025-12-27 21:32:45 -06:00
Jonathan Bennett
8757584f93 Add missing root_hash from scratch 2025-12-27 21:32:45 -06:00
Jonathan Bennett
83a49536c0 shorthash on canon announce 2025-12-27 21:32:45 -06:00
Jonathan Bennett
49c47c24e3 Build tryfix next 2025-12-27 21:32:45 -06:00
Jonathan Bennett
d847061d02 Misc fixes 2025-12-27 21:32:45 -06:00
Jonathan Bennett
451695a210 unbreak all the targets 2025-12-27 21:32:45 -06:00
Jonathan Bennett
20cd4a196c Store incoming non-canon messages in scratch 2025-12-27 21:32:45 -06:00
Jonathan Bennett
8575e13da8 Scratch fix 2025-12-27 21:32:45 -06:00
Jonathan Bennett
91588f5136 Broadcast root hash for an empty chain 2025-12-27 21:32:45 -06:00
Jonathan Bennett
82325a60ba Add Store and Forward++ module 2025-12-27 21:32:45 -06:00
40 changed files with 300 additions and 745 deletions

View File

@@ -240,7 +240,6 @@ jobs:
needs: [build]
steps:
- uses: actions/checkout@v6
if: github.event_name == 'pull_request_target'
with:
filter: blob:none # means we download all the git history but none of the commit (except ones with checkout like the head)
fetch-depth: 0
@@ -254,26 +253,19 @@ jobs:
uses: actions/upload-artifact@v6
id: upload-manifest
with:
name: manifests-${{ github.sha }}
name: manifests-all
overwrite: true
path: manifests-new/*.mt.json
path: |
manifests-new/*.mt.json
- name: Find the merge base
if: github.event_name == 'pull_request_target'
run: echo "MERGE_BASE=$(git merge-base "origin/$base" "$head")" >> $GITHUB_ENV
env:
base: ${{ github.base_ref }}
head: ${{ github.sha }}
# Currently broken (for-loop through EVERY artifact -- rate limiting)
# - name: Download the old manifests
# if: github.event_name == 'pull_request_target'
# run: gh run download -R "$repo" --name "manifests-$merge_base" --dir manifest-old/
# env:
# GH_TOKEN: ${{ github.token }}
# merge_base: ${{ env.MERGE_BASE }}
# repo: ${{ github.repository }}
# - name: Do scan and post comment
# if: github.event_name == 'pull_request_target'
# run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/
head: ${{ github.head_ref }}
- name: Download the old manifests
run: gh run download -R ${{ github.repository }} --commit ${{ env.MERGE_BASE }} --name manifests-all --dir manifest-old/
- name: Do scan and post comment
run: python3 bin/shame.py ${{ github.event.pull_request.number }} manifests-old/ manifests-new/
release-artifacts:
runs-on: ubuntu-latest

9
.gitignore vendored
View File

@@ -41,12 +41,3 @@ src/mesh/raspihttp/private_key.pem
# Ignore logo (set at build time with platformio-custom.py)
data/boot/logo.*
# pioarduino platform
managed_components/*
arduino-lib-builder*
dependencies.lock
idf_component.yml
CMakeLists.txt
sdkconfig.*
.dummy/*

View File

@@ -9,9 +9,9 @@ plugins:
lint:
enabled:
- checkov@3.2.495
- renovate@42.66.8
- renovate@42.64.1
- prettier@3.7.4
- trufflehog@3.92.4
- trufflehog@3.92.3
- yamllint@1.37.1
- bandit@1.9.2
- trivy@0.68.2

View File

@@ -21,14 +21,13 @@ rm -f $BUILDDIR/firmware*
export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION
ota_basename=${basename}-ota
pio run --environment $1 -t mtjson # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
echo "Copying NRF52 dfu (OTA) file"
cp $BUILDDIR/$basename.zip $OUTDIR/$ota_basename.zip
cp $BUILDDIR/$basename.zip $OUTDIR/$basename.zip
echo "Copying NRF52 UF2 file"
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2

View File

@@ -87,6 +87,9 @@
</screenshots>
<releases>
<release version="2.7.18" date="2025-12-20">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.18</url>
</release>
<release version="2.7.17" date="2025-11-28">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.17</url>
</release>

View File

@@ -17,8 +17,6 @@ lfsbin = f"{progname.replace('firmware-', 'littlefs-')}.bin"
def manifest_gather(source, target, env):
out = []
board_platform = env.BoardConfig().get("platform")
needs_ota_suffix = board_platform == "nordicnrf52"
check_paths = [
progname,
f"{progname}.elf",
@@ -34,11 +32,8 @@ def manifest_gather(source, target, env):
for p in check_paths:
f = env.File(env.subst(f"$BUILD_DIR/{p}"))
if f.exists():
manifest_name = p
if needs_ota_suffix and p == f"{progname}.zip":
manifest_name = f"{progname}-ota.zip"
d = {
"name": manifest_name,
"name": p,
"md5": f.get_content_hash(), # Returns MD5 hash
"bytes": f.get_size() # Returns file size in bytes
}

6
debian/changelog vendored
View File

@@ -1,3 +1,9 @@
meshtasticd (2.7.18.0) unstable; urgency=medium
* Version 2.7.18
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Sat, 20 Dec 2025 15:47:25 +0000
meshtasticd (2.7.17.0) unstable; urgency=medium
* Version 2.7.17

View File

@@ -64,7 +64,7 @@ monitor_speed = 115200
monitor_filters = direct
lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/b34c6817c25d6faabb3a8a162b5d14fb75395433.zip
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/2887bf4a19f64d92c984dcc8fd5ca7429e425e4a.zip
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master

View File

@@ -470,49 +470,18 @@ bool isAllowedPunctuation(char c)
return allowed.find(c) != std::string::npos;
}
static void replaceAll(std::string &s, const std::string &from, const std::string &to)
{
if (from.empty())
return;
size_t pos = 0;
while ((pos = s.find(from, pos)) != std::string::npos) {
s.replace(pos, from.size(), to);
pos += to.size();
}
}
std::string sanitizeString(const std::string &input)
{
std::string output;
bool inReplacement = false;
// Make a mutable copy so we can normalize UTF-8 “smart punctuation” into ASCII first.
std::string s = input;
// Curly single quotes:
replaceAll(s, "\xE2\x80\x98", "'"); // U+2018
replaceAll(s, "\xE2\x80\x99", "'"); // U+2019
// Curly double quotes: “ ”
replaceAll(s, "\xE2\x80\x9C", "\""); // U+201C
replaceAll(s, "\xE2\x80\x9D", "\""); // U+201D
// En dash / Em dash:
replaceAll(s, "\xE2\x80\x93", "-"); // U+2013
replaceAll(s, "\xE2\x80\x94", "-"); // U+2014
// Non-breaking space
replaceAll(s, "\xC2\xA0", " "); // U+00A0
// Now do your original sanitize pass over the normalized string.
for (unsigned char uc : s) {
char c = static_cast<char>(uc);
if (std::isalnum(uc) || isAllowedPunctuation(c)) {
for (char c : input) {
if (std::isalnum(static_cast<unsigned char>(c)) || isAllowedPunctuation(c)) {
output += c;
inReplacement = false;
} else {
if (!inReplacement) {
output += static_cast<char>(0xBF); // ISO-8859-1 for inverted question mark
output += 0xbf; // ISO-8859-1 for inverted question mark
inReplacement = true;
}
}

View File

@@ -1092,23 +1092,11 @@ void menuHandler::favoriteBaseMenu()
void menuHandler::positionBaseMenu()
{
enum optionsNumbers {
Back,
GPSToggle,
GPSFormat,
CompassMenu,
CompassCalibrate,
GPSSmartPosition,
GPSUpdateInterval,
GPSPositionBroadcast,
enumEnd
};
enum optionsNumbers { Back, GPSToggle, GPSFormat, CompassMenu, CompassCalibrate, enumEnd };
static const char *optionsArray[enumEnd] = {
"Back", "On/Off Toggle", "Format", "Smart Position", "Update Interval", "Broadcast Interval", "Compass"};
static int optionsEnumArray[enumEnd] = {
Back, GPSToggle, GPSFormat, GPSSmartPosition, GPSUpdateInterval, GPSPositionBroadcast, CompassMenu};
int options = 7;
static const char *optionsArray[enumEnd] = {"Back", "GPS Toggle", "GPS Format", "Compass"};
static int optionsEnumArray[enumEnd] = {Back, GPSToggle, GPSFormat, CompassMenu};
int options = 4;
if (accelerometerThread) {
optionsArray[options] = "Compass Calibrate";
@@ -1116,7 +1104,7 @@ void menuHandler::positionBaseMenu()
}
BannerOverlayOptions bannerOptions;
bannerOptions.message = "GPS Action";
bannerOptions.message = "Position Action";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options;
@@ -1132,15 +1120,6 @@ void menuHandler::positionBaseMenu()
screen->runNow();
} else if (selected == CompassCalibrate) {
accelerometerThread->calibrate(30);
} else if (selected == GPSSmartPosition) {
menuQueue = gps_smart_position_menu;
screen->runNow();
} else if (selected == GPSUpdateInterval) {
menuQueue = gps_update_interval_menu;
screen->runNow();
} else if (selected == GPSPositionBroadcast) {
menuQueue = gps_position_broadcast_menu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
@@ -1367,217 +1346,6 @@ void menuHandler::GPSFormatMenu()
bannerOptions.InitialSelected = uiconfig.gps_format + 1;
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::GPSSmartPositionMenu()
{
static const char *optionsArray[] = {"Back", "Enabled", "Disabled"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Toggle Smart Position";
if (currentResolution == ScreenResolution::UltraLow) {
bannerOptions.message = "Smrt Postn";
}
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) {
menuQueue = position_base_menu;
screen->runNow();
} else if (selected == 1) {
config.position.position_broadcast_smart_enabled = true;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
} else if (selected == 2) {
config.position.position_broadcast_smart_enabled = false;
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
};
bannerOptions.InitialSelected = config.position.position_broadcast_smart_enabled ? 1 : 2;
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::GPSUpdateIntervalMenu()
{
static const char *optionsArray[] = {"Back", "8 seconds", "20 seconds", "40 seconds", "1 minute", "80 seconds",
"2 minutes", "5 minutes", "10 minutes", "15 minutes", "30 minutes", "1 hour",
"6 hours", "12 hours", "24 hours", "At Boot Only"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Update Interval";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 16;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) {
menuQueue = position_base_menu;
screen->runNow();
} else if (selected == 1) {
config.position.gps_update_interval = 8;
} else if (selected == 2) {
config.position.gps_update_interval = 20;
} else if (selected == 3) {
config.position.gps_update_interval = 40;
} else if (selected == 4) {
config.position.gps_update_interval = 60;
} else if (selected == 5) {
config.position.gps_update_interval = 80;
} else if (selected == 6) {
config.position.gps_update_interval = 120;
} else if (selected == 7) {
config.position.gps_update_interval = 300;
} else if (selected == 8) {
config.position.gps_update_interval = 600;
} else if (selected == 9) {
config.position.gps_update_interval = 900;
} else if (selected == 10) {
config.position.gps_update_interval = 1800;
} else if (selected == 11) {
config.position.gps_update_interval = 3600;
} else if (selected == 12) {
config.position.gps_update_interval = 21600;
} else if (selected == 13) {
config.position.gps_update_interval = 43200;
} else if (selected == 14) {
config.position.gps_update_interval = 86400;
} else if (selected == 15) {
config.position.gps_update_interval = 2147483647; // At Boot Only
}
if (selected != 0) {
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
};
if (config.position.gps_update_interval == 8) {
bannerOptions.InitialSelected = 1;
} else if (config.position.gps_update_interval == 20) {
bannerOptions.InitialSelected = 2;
} else if (config.position.gps_update_interval == 40) {
bannerOptions.InitialSelected = 3;
} else if (config.position.gps_update_interval == 60) {
bannerOptions.InitialSelected = 4;
} else if (config.position.gps_update_interval == 80) {
bannerOptions.InitialSelected = 5;
} else if (config.position.gps_update_interval == 120) {
bannerOptions.InitialSelected = 6;
} else if (config.position.gps_update_interval == 300) {
bannerOptions.InitialSelected = 7;
} else if (config.position.gps_update_interval == 600) {
bannerOptions.InitialSelected = 8;
} else if (config.position.gps_update_interval == 900) {
bannerOptions.InitialSelected = 9;
} else if (config.position.gps_update_interval == 1800) {
bannerOptions.InitialSelected = 10;
} else if (config.position.gps_update_interval == 3600) {
bannerOptions.InitialSelected = 11;
} else if (config.position.gps_update_interval == 21600) {
bannerOptions.InitialSelected = 12;
} else if (config.position.gps_update_interval == 43200) {
bannerOptions.InitialSelected = 13;
} else if (config.position.gps_update_interval == 86400) {
bannerOptions.InitialSelected = 14;
} else if (config.position.gps_update_interval == 2147483647) { // At Boot Only
bannerOptions.InitialSelected = 15;
} else {
bannerOptions.InitialSelected = 0;
}
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::GPSPositionBroadcastMenu()
{
static const char *optionsArray[] = {"Back", "1 minute", "90 seconds", "5 minutes", "15 minutes", "1 hour",
"2 hours", "3 hours", "4 hours", "5 hours", "6 hours", "12 hours",
"18 hours", "24 hours", "36 hours", "48 hours", "72 hours"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Broadcast Interval";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 17;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) {
menuQueue = position_base_menu;
screen->runNow();
} else if (selected == 1) {
config.position.position_broadcast_secs = 60;
} else if (selected == 2) {
config.position.position_broadcast_secs = 90;
} else if (selected == 3) {
config.position.position_broadcast_secs = 300;
} else if (selected == 4) {
config.position.position_broadcast_secs = 900;
} else if (selected == 5) {
config.position.position_broadcast_secs = 3600;
} else if (selected == 6) {
config.position.position_broadcast_secs = 7200;
} else if (selected == 7) {
config.position.position_broadcast_secs = 10800;
} else if (selected == 8) {
config.position.position_broadcast_secs = 14400;
} else if (selected == 9) {
config.position.position_broadcast_secs = 18000;
} else if (selected == 10) {
config.position.position_broadcast_secs = 21600;
} else if (selected == 11) {
config.position.position_broadcast_secs = 43200;
} else if (selected == 12) {
config.position.position_broadcast_secs = 64800;
} else if (selected == 13) {
config.position.position_broadcast_secs = 86400;
} else if (selected == 14) {
config.position.position_broadcast_secs = 129600;
} else if (selected == 15) {
config.position.position_broadcast_secs = 172800;
} else if (selected == 16) {
config.position.position_broadcast_secs = 259200;
}
if (selected != 0) {
saveUIConfig();
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
};
if (config.position.position_broadcast_secs == 60) {
bannerOptions.InitialSelected = 1;
} else if (config.position.position_broadcast_secs == 90) {
bannerOptions.InitialSelected = 2;
} else if (config.position.position_broadcast_secs == 300) {
bannerOptions.InitialSelected = 3;
} else if (config.position.position_broadcast_secs == 900) {
bannerOptions.InitialSelected = 4;
} else if (config.position.position_broadcast_secs == 3600) {
bannerOptions.InitialSelected = 5;
} else if (config.position.position_broadcast_secs == 7200) {
bannerOptions.InitialSelected = 6;
} else if (config.position.position_broadcast_secs == 10800) {
bannerOptions.InitialSelected = 7;
} else if (config.position.position_broadcast_secs == 14400) {
bannerOptions.InitialSelected = 8;
} else if (config.position.position_broadcast_secs == 18000) {
bannerOptions.InitialSelected = 9;
} else if (config.position.position_broadcast_secs == 21600) {
bannerOptions.InitialSelected = 10;
} else if (config.position.position_broadcast_secs == 43200) {
bannerOptions.InitialSelected = 11;
} else if (config.position.position_broadcast_secs == 64800) {
bannerOptions.InitialSelected = 12;
} else if (config.position.position_broadcast_secs == 86400) {
bannerOptions.InitialSelected = 13;
} else if (config.position.position_broadcast_secs == 129600) {
bannerOptions.InitialSelected = 14;
} else if (config.position.position_broadcast_secs == 172800) {
bannerOptions.InitialSelected = 15;
} else if (config.position.position_broadcast_secs == 259200) {
bannerOptions.InitialSelected = 16;
} else {
bannerOptions.InitialSelected = 0;
}
screen->showOverlayBanner(bannerOptions);
}
#endif
void menuHandler::BluetoothToggleMenu()
@@ -2358,15 +2126,6 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case gps_format_menu:
GPSFormatMenu();
break;
case gps_smart_position_menu:
GPSSmartPositionMenu();
break;
case gps_update_interval_menu:
GPSUpdateIntervalMenu();
break;
case gps_position_broadcast_menu:
GPSPositionBroadcastMenu();
break;
#endif
case compass_point_north_menu:
compassNorthMenu();

View File

@@ -22,9 +22,6 @@ class menuHandler
node_base_menu,
gps_toggle_menu,
gps_format_menu,
gps_smart_position_menu,
gps_update_interval_menu,
gps_position_broadcast_menu,
compass_point_north_menu,
reset_node_db_menu,
buzzermodemenupicker,
@@ -80,9 +77,6 @@ class menuHandler
static void compassNorthMenu();
static void GPSToggleMenu();
static void GPSFormatMenu();
static void GPSSmartPositionMenu();
static void GPSUpdateIntervalMenu();
static void GPSPositionBroadcastMenu();
static void BuzzerModeMenu();
static void switchToMUIMenu();
static void TFTColorPickerMenu(OLEDDisplay *display);

View File

@@ -27,9 +27,7 @@ bool RotaryEncoderInterruptImpl1::init()
RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB,
RotaryEncoderInterruptImpl1::handleIntPressed);
inputBroker->registerSource(this);
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true;
#endif
return true;
}

View File

@@ -45,9 +45,7 @@ void TrackballInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLef
LOG_DEBUG("Trackball GPIO initialized - UP:%d DOWN:%d LEFT:%d RIGHT:%d PRESS:%d", this->_pinUp, this->_pinDown,
this->_pinLeft, this->_pinRight, pinPress);
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true;
#endif
this->setInterval(100);
}

View File

@@ -29,9 +29,7 @@ bool UpDownInterruptImpl1::init()
eventDownLong, UpDownInterruptImpl1::handleIntDown, UpDownInterruptImpl1::handleIntUp,
UpDownInterruptImpl1::handleIntPressed);
inputBroker->registerSource(this);
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true;
#endif
return true;
}

View File

@@ -1186,6 +1186,11 @@ void setup()
#endif
#endif
#ifdef PIN_PWR_DELAY_MS
// This may be required to give the peripherals time to power up.
delay(PIN_PWR_DELAY_MS);
#endif
#ifdef ARCH_PORTDUINO
// as one can't use a function pointer to the class constructor:
auto loraModuleInterface = [](LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst,
@@ -1460,10 +1465,8 @@ void setup()
#endif
#if defined(HAS_TRACKBALL) || (defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2)
#ifndef HAS_PHYSICAL_KEYBOARD
osk_found = true;
#endif
#endif
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER
// Start web server thread.

View File

@@ -195,13 +195,15 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p)
p.rx_time = getValidTime(RTCQualityFromNet); // Record the time the packet arrived from the phone
IF_SCREEN(if (p.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP && p.decoded.payload.size > 0 &&
p.to != NODENUM_BROADCAST && p.to != 0) // DM only
{
perhapsDecode(&p);
const StoredMessage &sm = messageStore.addFromPacket(p);
graphics::MessageRenderer::handleNewMessage(nullptr, sm, p); // notify UI
})
#if HAS_SCREEN
if (screen && p.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP && p.decoded.payload.size > 0 &&
p.to != NODENUM_BROADCAST && p.to != 0) // DM only
{
perhapsDecode(&p);
const StoredMessage &sm = messageStore.addFromPacket(p);
graphics::MessageRenderer::handleNewMessage(nullptr, sm, p); // notify UI
}
#endif
// Send the packet into the mesh
DEBUG_HEAP_BEFORE;
auto a = packetPool.allocCopy(p);

View File

@@ -805,8 +805,7 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.output_ms = 500;
moduleConfig.external_notification.nag_timeout = 2;
#endif
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3) || \
defined(ELECROW_ThinkNode_M6)
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE) || defined(ELECROW_ThinkNode_M3)
// Default to PIN_LED2 for external notification output (LED color depends on device variant)
moduleConfig.external_notification.enabled = true;
moduleConfig.external_notification.output = PIN_LED2;

View File

@@ -730,8 +730,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
meshtastic_PortNum_POSITION_APP, meshtastic_PortNum_NODEINFO_APP, meshtastic_PortNum_ROUTING_APP,
meshtastic_PortNum_TELEMETRY_APP, meshtastic_PortNum_ADMIN_APP, meshtastic_PortNum_ALERT_APP,
meshtastic_PortNum_KEY_VERIFICATION_APP, meshtastic_PortNum_WAYPOINT_APP,
meshtastic_PortNum_STORE_FORWARD_APP, meshtastic_PortNum_TRACEROUTE_APP,
meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP)) {
meshtastic_PortNum_STORE_FORWARD_APP, meshtastic_PortNum_TRACEROUTE_APP)) {
LOG_DEBUG("Ignore packet on non-standard portnum for CORE_PORTNUMS_ONLY");
cancelSending(p->from, p->id);
skipHandle = true;

View File

@@ -33,5 +33,3 @@ PB_BIND(meshtastic_KeyVerificationAdmin, meshtastic_KeyVerificationAdmin, AUTO)

View File

@@ -16,16 +16,6 @@
#endif
/* Enum definitions */
/* Firmware update mode for OTA updates */
typedef enum _meshtastic_OTAMode {
/* Do not reboot into OTA mode */
meshtastic_OTAMode_NO_REBOOT_OTA = 0,
/* Reboot into OTA mode for BLE firmware update */
meshtastic_OTAMode_OTA_BLE = 1,
/* Reboot into OTA mode for WiFi firmware update */
meshtastic_OTAMode_OTA_WIFI = 2
} meshtastic_OTAMode;
/* TODO: REPLACE */
typedef enum _meshtastic_AdminMessage_ConfigType {
/* TODO: REPLACE */
@@ -268,13 +258,10 @@ typedef struct _meshtastic_AdminMessage {
meshtastic_SharedContact add_contact;
/* Initiate or respond to a key verification request */
meshtastic_KeyVerificationAdmin key_verification;
/* Tell the node to reboot into OTA mode for firmware update via BLE or WiFi (ESP32 only for now) */
meshtastic_OTAMode reboot_ota_mode;
/* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */
int32_t factory_reset_device;
/* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth.
Deprecated in favor of reboot_ota_mode in 2.7.17 */
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */
int32_t reboot_ota_seconds;
/* This message is only supported for the simulator Portduino build.
If received the simulator will exit successfully. */
@@ -301,10 +288,6 @@ extern "C" {
#endif
/* Helper constants for enums */
#define _meshtastic_OTAMode_MIN meshtastic_OTAMode_NO_REBOOT_OTA
#define _meshtastic_OTAMode_MAX meshtastic_OTAMode_OTA_WIFI
#define _meshtastic_OTAMode_ARRAYSIZE ((meshtastic_OTAMode)(meshtastic_OTAMode_OTA_WIFI+1))
#define _meshtastic_AdminMessage_ConfigType_MIN meshtastic_AdminMessage_ConfigType_DEVICE_CONFIG
#define _meshtastic_AdminMessage_ConfigType_MAX meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG
#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG+1))
@@ -326,7 +309,6 @@ extern "C" {
#define meshtastic_AdminMessage_payload_variant_backup_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
#define meshtastic_AdminMessage_payload_variant_restore_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
#define meshtastic_AdminMessage_payload_variant_remove_backup_preferences_ENUMTYPE meshtastic_AdminMessage_BackupLocation
#define meshtastic_AdminMessage_payload_variant_reboot_ota_mode_ENUMTYPE meshtastic_OTAMode
@@ -414,7 +396,6 @@ extern "C" {
#define meshtastic_AdminMessage_commit_edit_settings_tag 65
#define meshtastic_AdminMessage_add_contact_tag 66
#define meshtastic_AdminMessage_key_verification_tag 67
#define meshtastic_AdminMessage_reboot_ota_mode_tag 68
#define meshtastic_AdminMessage_factory_reset_device_tag 94
#define meshtastic_AdminMessage_reboot_ota_seconds_tag 95
#define meshtastic_AdminMessage_exit_simulator_tag 96
@@ -473,7 +454,6 @@ X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_ed
X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,add_contact,add_contact), 66) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,key_verification,key_verification), 67) \
X(a, STATIC, ONEOF, UENUM, (payload_variant,reboot_ota_mode,reboot_ota_mode), 68) \
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \
X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \

View File

@@ -245,9 +245,7 @@ void setupModules()
#if ARCH_PORTDUINO
new HostMetricsModule();
#if SFPP_ENABLED
if (portduino_config.sfpp_enabled) {
new StoreForwardPlusPlusModule();
}
new StoreForwardPlusPlusModule();
#endif
#endif
#if HAS_TELEMETRY

View File

@@ -1,4 +1,12 @@
// Eventual TODO: non-stratum0 nodes need to be pointed at their upstream source? Maybe
// I've done a lot of this in SQLite for now, but honestly it needs to happen in memory, and get saved to sqlite during downtime
// TODO: custom hops. 1 maybe 0. Configurable?
// TODO: non-stratum0 nodes need to be pointed at their upstream source? Maybe
// TODO: evict messages from scratch after a timeout
// things may get weird if there are multiple stratum-0 nodes on a single mesh. Come up with mitigations
#if __has_include("sqlite3.h")
#include "StoreForwardPlusPlus.h"
@@ -9,17 +17,16 @@
#include "modules/RoutingModule.h"
StoreForwardPlusPlusModule::StoreForwardPlusPlusModule()
: ProtobufModule("StoreForwardpp",
portduino_config.sfpp_steal_port ? meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP
: meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP,
&meshtastic_StoreForwardPlusPlus_msg),
: ProtobufModule("StoreForwardpp", meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP, &meshtastic_StoreForwardPlusPlus_msg),
concurrency::OSThread("StoreForwardpp")
{
LOG_WARN("StoreForwardPlusPlusModule init");
std::string db_path = portduino_config.sfpp_db_path + "storeforwardpp.db";
LOG_INFO("Opening StoreForwardpp DB at %s", db_path.c_str());
if (portduino_config.sfpp_stratum0)
LOG_INFO("SF++ running as stratum0");
LOG_WARN("SF++ stratum0");
std::string db_path = portduino_config.sfpp_db_path + "storeforwardpp.db";
LOG_WARN("Opening SF++ DB at %s", db_path.c_str());
int res = sqlite3_open(db_path.c_str(), &ppDb);
if (res != SQLITE_OK) {
LOG_ERROR("Cannot open database: %s", sqlite3_errmsg(ppDb));
@@ -33,11 +40,12 @@ StoreForwardPlusPlusModule::StoreForwardPlusPlusModule()
ppDb = nullptr;
exit(EXIT_FAILURE);
}
LOG_WARN("Result1 %u", res);
char *err = nullptr;
res = sqlite3_exec(ppDb, " \
CREATE TABLE IF NOT EXISTS \
channel_messages( \
CREATE TABLE channel_messages( \
destination INT NOT NULL, \
sender INT NOT NULL, \
packet_id INT NOT NULL, \
@@ -51,43 +59,37 @@ StoreForwardPlusPlusModule::StoreForwardPlusPlusModule()
PRIMARY KEY (message_hash) \
);",
NULL, NULL, &err);
if (res != SQLITE_OK) {
LOG_ERROR("Failed to create table: %s", sqlite3_errmsg(ppDb));
}
LOG_WARN("Result2 %u", res);
if (err != nullptr)
LOG_ERROR("%s", err);
sqlite3_free(err);
res = sqlite3_exec(ppDb, " \
CREATE TABLE IF NOT EXISTS \
local_messages( \
CREATE TABLE local_messages( \
destination INT NOT NULL, \
sender INT NOT NULL, \
packet_id INT NOT NULL, \
rx_time INT NOT NULL, \
root_hash BLOB NOT NULL, \
root_hash BLOB NOT NULL, \
encrypted_bytes BLOB NOT NULL, \
message_hash BLOB NOT NULL, \
payload TEXT, \
PRIMARY KEY (message_hash) \
);",
NULL, NULL, &err);
if (res != SQLITE_OK) {
LOG_ERROR("Failed to create table: %s", sqlite3_errmsg(ppDb));
}
LOG_WARN("Result2 %u", res);
if (err != nullptr)
LOG_ERROR("%s", err);
sqlite3_free(err);
// create table DMs
res = sqlite3_exec(ppDb, " \
CREATE TABLE IF NOT EXISTS \
direct_messages( \
CREATE TABLE direct_messages( \
destination INT NOT NULL, \
sender INT NOT NULL, \
packet_id INT NOT NULL, \
rx_time INT NOT NULL, \
root_hash BLOB NOT NULL, \
root_hash BLOB NOT NULL, \
commit_hash BLOB NOT NULL, \
encrypted_bytes BLOB NOT NULL, \
message_hash BLOB NOT NULL, \
@@ -95,29 +97,22 @@ StoreForwardPlusPlusModule::StoreForwardPlusPlusModule()
PRIMARY KEY (message_hash) \
);",
NULL, NULL, &err);
if (res != SQLITE_OK) {
LOG_ERROR("Failed to create table: %s", sqlite3_errmsg(ppDb));
}
LOG_WARN("Result2 %u", res);
if (err != nullptr)
LOG_ERROR("%s", err);
sqlite3_free(err);
// mappings table -- connects the root hashes to channel hashes and DM identifiers
res = sqlite3_exec(ppDb, " \
CREATE TABLE IF NOT EXISTS \
mappings( \
chain_type INT NOT NULL, \
identifier INT NOT NULL, \
root_hash BLOB NOT NULL, \
count INT DEFAULT 0, \
PRIMARY KEY (identifier) \
CREATE TABLE mappings( \
chain_type INT NOT NULL, \
identifier INT NOT NULL, \
root_hash BLOB NOT NULL, \
count INT DEFAULT 0, \
PRIMARY KEY (identifier) \
);",
NULL, NULL, &err);
if (res != SQLITE_OK) {
LOG_ERROR("Failed to create table: %s", sqlite3_errmsg(ppDb));
}
LOG_WARN("Result2 %u", res);
if (err != nullptr)
LOG_ERROR("%s", err);
sqlite3_free(err);
@@ -176,15 +171,7 @@ StoreForwardPlusPlusModule::StoreForwardPlusPlusModule()
sqlite3_prepare_v2(ppDb, "UPDATE mappings SET count=? WHERE substr(root_hash,1,?)=?;", -1, &setChainCountStmt, NULL);
sqlite3_prepare_v2(ppDb, "SELECT count(*) FROM channel_messages WHERE substr(root_hash,1,?)=?;", -1, &getChainCountStmt,
NULL);
sqlite3_prepare_v2(ppDb, "DELETE FROM local_messages WHERE rx_time < ?;", -1, &pruneScratchQueueStmt, NULL);
sqlite3_prepare_v2(ppDb,
"DELETE FROM channel_messages WHERE commit_hash in ( select commit_hash from channel_messages where "
"substr(root_hash,1,?)=? ORDER BY rowid ASC LIMIT 1);",
-1, &trimOldestLinkStmt, NULL);
sqlite3_prepare_v2(ppDb, "SELECT count FROM mappings WHERE substr(root_hash,1,?)=?;", -1, &getChainCountStmt, NULL);
encryptedOk = true;
@@ -193,6 +180,9 @@ StoreForwardPlusPlusModule::StoreForwardPlusPlusModule()
int32_t StoreForwardPlusPlusModule::runOnce()
{
// get number of links on chain
// if more than max_chain, evict oldest
LOG_WARN("StoreForward++ runONce");
pendingRun = false;
if (getRTCQuality() < RTCQualityNTP) {
LOG_WARN("StoreForward++ deferred due to time quality %u", getRTCQuality());
@@ -202,36 +192,26 @@ int32_t StoreForwardPlusPlusModule::runOnce()
ChannelHash hash = channels.getHash(0);
getOrAddRootFromChannelHash(hash, root_hash_bytes);
uint32_t chain_count = getChainCount(root_hash_bytes, SFPP_HASH_SIZE);
LOG_DEBUG("Chain count is %u", chain_count);
while (chain_count > portduino_config.sfpp_max_chain) {
LOG_DEBUG("Chain length %u exceeds max %u, evicting oldest", chain_count, portduino_config.sfpp_max_chain);
trimOldestLink(root_hash_bytes, SFPP_HASH_SIZE);
chain_count--;
LOG_WARN("Chain count is %u", chain_count);
if (chain_count > portduino_config.sfpp_max_chain) {
LOG_WARN("Chain length %u exceeds max %u, evicting oldest", chain_count, portduino_config.sfpp_max_chain);
}
// evict old messages from scratch
pruneScratchQueue();
if (memfll(root_hash_bytes, '\0', SFPP_HASH_SIZE)) {
LOG_WARN("No root hash found, not sending");
return portduino_config.sfpp_announce_interval * 60 * 1000;
}
if (doing_split_send) {
LOG_DEBUG("Sending split second half");
broadcastLink(split_link_out, true, true);
split_link_out = link_object();
split_link_out.validObject = false;
return portduino_config.sfpp_announce_interval * 60 * 1000;
}
// get tip of chain for this channel
link_object chain_end = getLinkFromCount(0, root_hash_bytes, SFPP_HASH_SIZE);
LOG_WARN("latest payload %s", chain_end.payload.c_str());
if (chain_end.rx_time == 0) {
LOG_WARN("Store and Forward++ database lookup returned null");
if (portduino_config.sfpp_stratum0) {
LOG_DEBUG("Stratum0 with no messages on chain, sending empty announce");
LOG_WARN("Stratum0 with no messages on chain, sending empty announce");
} else {
LOG_DEBUG("Non-stratum0 with no chain, not sending");
LOG_WARN("Non-stratum0 with no chain, not sending");
return portduino_config.sfpp_announce_interval * 60 * 1000;
}
@@ -250,7 +230,7 @@ int32_t StoreForwardPlusPlusModule::runOnce()
p->channel = 0;
p->hop_limit = portduino_config.sfpp_hops;
p->hop_start = portduino_config.sfpp_hops;
LOG_INFO("Send packet to mesh payload size %u", p->decoded.payload.size);
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
return portduino_config.sfpp_announce_interval * 60 * 1000;
@@ -282,11 +262,12 @@ ProcessMessage StoreForwardPlusPlusModule::handleReceived(const meshtastic_MeshP
if (mp.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
LOG_WARN("in handleReceived");
if (mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP && mp.to == NODENUM_BROADCAST) {
link_object lo = ingestTextPacket(mp, router->p_encrypted);
if (isInDB(lo.message_hash, lo.message_hash_len)) {
LOG_DEBUG("Found text message in chain DB");
LOG_WARN("found message in db");
// We may have this message already, but we may not have the payload
// if we do, we can update the payload in the database
if (lo.payload != "")
@@ -297,11 +278,11 @@ ProcessMessage StoreForwardPlusPlusModule::handleReceived(const meshtastic_MeshP
if (!portduino_config.sfpp_stratum0) {
if (!isInDB(lo.message_hash, lo.message_hash_len)) {
if (lo.root_hash_len == 0) {
LOG_DEBUG("Received text message, but no chain. Possibly no Stratum0 on local mesh.");
LOG_WARN("Received message, but no known chain");
return ProcessMessage::CONTINUE;
}
addToScratch(lo);
LOG_DEBUG("added message to scratch db");
LOG_WARN("added message to scratch");
// send link to upstream?
}
return ProcessMessage::CONTINUE;
@@ -315,8 +296,8 @@ ProcessMessage StoreForwardPlusPlusModule::handleReceived(const meshtastic_MeshP
// canonAnnounce(lo.message_hash, lo.commit_hash, lo.root_hash, lo.rx_time);
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
// TODO: Block packets from self?
} else if (mp.decoded.portnum == portduino_config.sfpp_steal_port ? meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP
: meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP) {
} else if (mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
LOG_WARN("Got a STORE_FORWARD++ packet");
meshtastic_StoreForwardPlusPlus scratch;
pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_StoreForwardPlusPlus_fields, &scratch);
handleReceivedProtobuf(mp, &scratch);
@@ -327,13 +308,11 @@ ProcessMessage StoreForwardPlusPlusModule::handleReceived(const meshtastic_MeshP
bool StoreForwardPlusPlusModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_StoreForwardPlusPlus *t)
{
LOG_DEBUG("StoreForwardpp node %u sent us sf++ packet", mp.from);
LOG_WARN("in handleReceivedProtobuf");
LOG_WARN("Sfp++ node %u sent us sf++ packet", mp.from);
printBytes("commit_hash ", t->commit_hash.bytes, t->commit_hash.size);
printBytes("root_hash ", t->root_hash.bytes, t->root_hash.size);
link_object incoming_link;
incoming_link.validObject = false;
if (t->sfpp_message_type == meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE) {
// TODO: Regardless of where we are in the chain, if we have a newer message, send it back.
@@ -349,19 +328,20 @@ bool StoreForwardPlusPlusModule::handleReceivedProtobuf(const meshtastic_MeshPac
} else {
uint8_t tmp_root_hash_bytes[SFPP_HASH_SIZE] = {0};
LOG_DEBUG("Received a CANON_ANNOUNCE");
LOG_WARN("Received a CANON_ANNOUNCE");
if (getRootFromChannelHash(router->p_encrypted->channel, tmp_root_hash_bytes)) {
// we found the hash, check if it's the right one
if (memcmp(tmp_root_hash_bytes, t->root_hash.bytes, t->root_hash.size) != 0) {
LOG_INFO("Root hash does not match. Possibly two stratum0 nodes on the mesh?");
LOG_WARN("Found root hash, and it doesn't match!");
return true;
}
} else {
// TODO: size check
addRootToMappings(router->p_encrypted->channel, t->root_hash.bytes);
LOG_DEBUG("Adding root hash to mappings");
LOG_WARN("Adding root hash to mappings");
}
if (t->encapsulated_rxtime == 0) {
LOG_DEBUG("No encapsulated time, conclude the chain is empty");
LOG_WARN("No encapsulated time, conclude the chain is empty");
return true;
}
@@ -370,11 +350,12 @@ bool StoreForwardPlusPlusModule::handleReceivedProtobuf(const meshtastic_MeshPac
// get chain tip
if (chain_end.rx_time != 0) {
// TODO: size check
if (memcmp(chain_end.commit_hash, t->commit_hash.bytes, t->commit_hash.size) == 0) {
LOG_DEBUG("End of chain matches!");
LOG_WARN("End of chain matches!");
sendFromScratch(chain_end.root_hash);
} else {
LOG_DEBUG("End of chain does not match!");
LOG_INFO("End of chain does not match!");
// We just got an end of chain announce, checking if we have seen this message and have it in scratch.
if (isInScratch(t->message_hash.bytes, t->message_hash.size)) {
@@ -383,7 +364,7 @@ bool StoreForwardPlusPlusModule::handleReceivedProtobuf(const meshtastic_MeshPac
// we know exactly what it is
if (t->message_hash.size >= 8 &&
checkCommitHash(scratch_object, t->commit_hash.bytes, t->message_hash.size)) {
scratch_object.rx_time = t->encapsulated_rxtime;
addToChain(scratch_object);
removeFromScratch(scratch_object.message_hash, scratch_object.message_hash_len);
// short circuit and return
@@ -396,8 +377,9 @@ bool StoreForwardPlusPlusModule::handleReceivedProtobuf(const meshtastic_MeshPac
}
}
} else { // if chainEnd()
LOG_WARN("No Messages on this chain, request!");
// todo request using portduino config initial_sync
if (airTime->isTxAllowedChannelUtil(true)) {
LOG_DEBUG("New chain, requesting last %u messages", portduino_config.sfpp_initial_sync);
requestMessageCount(t->root_hash.bytes, t->root_hash.size, portduino_config.sfpp_initial_sync);
}
}
@@ -405,12 +387,12 @@ bool StoreForwardPlusPlusModule::handleReceivedProtobuf(const meshtastic_MeshPac
} else if (t->sfpp_message_type == meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_REQUEST) {
uint8_t next_commit_hash[SFPP_HASH_SIZE] = {0};
LOG_DEBUG("Received link request");
LOG_WARN("Received link request");
// If chain_count is set, this is a request for x messages up the chain.
if (t->chain_count != 0 && t->root_hash.size >= 8) {
link_object link_from_count = getLinkFromCount(t->chain_count, t->root_hash.bytes, t->root_hash.size);
LOG_DEBUG("Count requested %d", t->chain_count);
LOG_WARN("Count requested %d", t->chain_count);
if (link_from_count.validObject)
broadcastLink(link_from_count, true);
@@ -423,58 +405,12 @@ bool StoreForwardPlusPlusModule::handleReceivedProtobuf(const meshtastic_MeshPac
// if root and chain hashes are the same, grab the first message on the chain
// if different, get the message directly after.
// check if the root
} else if (t->sfpp_message_type == meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE) {
LOG_DEBUG("Link Provide received!");
LOG_WARN("Link Provide received!");
link_object incoming_link = ingestLinkMessage(t);
} else if (t->sfpp_message_type == meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_FIRSTHALF) {
LOG_DEBUG("Link Provide First Half received!");
split_link_in = ingestLinkMessage(t, false);
doing_split_receive = true;
split_link_in.validObject = true;
return true;
} else if (t->sfpp_message_type == meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF) {
LOG_DEBUG("Link Provide Second Half received!");
if (!doing_split_receive) {
LOG_DEBUG("Received second half without first half, ignoring");
return true;
}
if (!split_link_in.validObject) {
LOG_WARN("No first half stored, cannot combine");
doing_split_receive = false;
return true;
}
link_object second_half = ingestLinkMessage(t, false);
if (split_link_in.encrypted_len + second_half.encrypted_len > 256) {
LOG_WARN("Combined link too large");
return true;
}
if (split_link_in.from == second_half.from && split_link_in.to == second_half.to &&
split_link_in.root_hash_len == second_half.root_hash_len &&
memcmp(split_link_in.root_hash, second_half.root_hash, split_link_in.root_hash_len) == 0 &&
split_link_in.message_hash_len == second_half.message_hash_len &&
memcmp(split_link_in.message_hash, second_half.message_hash, split_link_in.message_hash_len) == 0) {
incoming_link = split_link_in;
memcpy(&incoming_link.encrypted_bytes[split_link_in.encrypted_len], second_half.encrypted_bytes,
second_half.encrypted_len);
incoming_link.encrypted_len = split_link_in.encrypted_len + second_half.encrypted_len;
// append the encrypted bytes
// clear first half
split_link_in = link_object();
split_link_in.validObject = false;
doing_split_receive = false;
} else {
LOG_WARN("No first half stored, cannot combine");
return true;
}
}
if (incoming_link.validObject) {
if (incoming_link.root_hash_len == 0) {
LOG_WARN("Hash bytes not found for incoming link");
return true;
@@ -487,7 +423,7 @@ bool StoreForwardPlusPlusModule::handleReceivedProtobuf(const meshtastic_MeshPac
if (portduino_config.sfpp_stratum0) {
if (isInDB(incoming_link.message_hash, incoming_link.message_hash_len)) {
LOG_INFO("Received link already in chain");
LOG_WARN("Received link already in chain");
// TODO: respond with next link?
return true;
}
@@ -516,16 +452,17 @@ bool StoreForwardPlusPlusModule::handleReceivedProtobuf(const meshtastic_MeshPac
} else {
// if this packet is new to us, we rebroadcast it, but only up to an hour old
if (incoming_link.rx_time > getValidTime(RTCQuality::RTCQualityNTP, true) - rebroadcastTimeout) {
LOG_WARN("Attempting to Rebroadcast message");
rebroadcastLinkObject(incoming_link);
}
}
requestNextMessage(incoming_link.root_hash, incoming_link.root_hash_len, incoming_link.commit_hash,
incoming_link.commit_hash_len);
requestNextMessage(t->root_hash.bytes, t->root_hash.size, t->commit_hash.bytes, t->commit_hash.size);
} else {
if (!isInScratch(incoming_link.message_hash, incoming_link.message_hash_len)) {
addToScratch(incoming_link);
LOG_INFO("added incoming non-canon message to scratch");
LOG_WARN("added incoming non-canon message to scratch");
if (incoming_link.rx_time > getValidTime(RTCQuality::RTCQualityNTP, true) - rebroadcastTimeout) {
LOG_WARN("Attempting to Rebroadcast message");
rebroadcastLinkObject(incoming_link);
}
}
@@ -543,6 +480,7 @@ bool StoreForwardPlusPlusModule::getRootFromChannelHash(ChannelHash _ch_hash, ui
sqlite3_step(getRootFromChannelHashStmt);
uint8_t *tmp_root_hash = (uint8_t *)sqlite3_column_blob(getRootFromChannelHashStmt, 0);
if (tmp_root_hash) {
LOG_WARN("Found root hash!");
memcpy(_root_hash, tmp_root_hash, SFPP_HASH_SIZE);
found = true;
}
@@ -563,11 +501,13 @@ ChannelHash StoreForwardPlusPlusModule::getChannelHashFromRoot(uint8_t *_root_ha
// return code indicates bytes in root hash, or 0 if not found/added
size_t StoreForwardPlusPlusModule::getOrAddRootFromChannelHash(ChannelHash _ch_hash, uint8_t *_root_hash)
{
LOG_WARN("getOrAddRootFromChannelHash()");
bool wasFound = getRootFromChannelHash(_ch_hash, _root_hash);
if (!wasFound) {
if (portduino_config.sfpp_stratum0) {
LOG_INFO("Generating Root hash!");
LOG_WARN("Generating Root hash!");
// generate root hash
SHA256 root_hash;
root_hash.update(&_ch_hash, sizeof(_ch_hash));
NodeNum ourNode = nodeDB->getNodeNum();
@@ -587,7 +527,13 @@ size_t StoreForwardPlusPlusModule::getOrAddRootFromChannelHash(ChannelHash _ch_h
void StoreForwardPlusPlusModule::addRootToMappings(ChannelHash _ch_hash, uint8_t *_root_hash)
{
LOG_WARN("addRootToMappings()");
printBytes("_root_hash", _root_hash, SFPP_HASH_SIZE);
// write to the table
int type = chain_types::channel_chain;
// note, must be an int variable
sqlite3_bind_int(addRootToMappingsStmt, 1, type);
sqlite3_bind_int(addRootToMappingsStmt, 2, _ch_hash);
sqlite3_bind_blob(addRootToMappingsStmt, 3, _root_hash, SFPP_HASH_SIZE, NULL);
@@ -621,9 +567,11 @@ void StoreForwardPlusPlusModule::requestNextMessage(uint8_t *_root_hash, size_t
p->channel = 0;
p->hop_limit = portduino_config.sfpp_hops;
p->hop_start = portduino_config.sfpp_hops;
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
}
// todo requestMessageCount
void StoreForwardPlusPlusModule::requestMessageCount(uint8_t *_root_hash, size_t _root_hash_len, uint32_t count)
{
@@ -645,12 +593,18 @@ void StoreForwardPlusPlusModule::requestMessageCount(uint8_t *_root_hash, size_t
p->channel = 0;
p->hop_limit = portduino_config.sfpp_hops;
p->hop_start = portduino_config.sfpp_hops;
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
}
bool StoreForwardPlusPlusModule::getNextHash(uint8_t *_root_hash, size_t _root_hash_len, uint8_t *_commit_hash,
size_t _commit_hash_len, uint8_t *next_commit_hash)
{
LOG_WARN("getNextHash");
// ChannelHash _channel_hash = getChannelHashFromRoot(_root_hash, _root_hash_len);
// LOG_WARN("_channel_hash %u", _channel_hash);
int rc;
sqlite3_bind_int(getNextHashStmt, 1, _root_hash_len);
sqlite3_bind_blob(getNextHashStmt, 2, _root_hash, _root_hash_len, NULL);
@@ -660,10 +614,11 @@ bool StoreForwardPlusPlusModule::getNextHash(uint8_t *_root_hash, size_t _root_h
if (memcmp(_root_hash, _commit_hash, _commit_hash_len) == 0) {
rc = sqlite3_step(getNextHashStmt);
if (rc != SQLITE_OK) {
LOG_WARN("Get Hash error %u, %s", rc, sqlite3_errmsg(ppDb));
LOG_WARN("here2 %u, %s", rc, sqlite3_errmsg(ppDb));
}
uint8_t *tmp_commit_hash = (uint8_t *)sqlite3_column_blob(getNextHashStmt, 0);
if (tmp_commit_hash == nullptr) {
LOG_WARN("No next hash found");
sqlite3_reset(getNextHashStmt);
return false;
}
@@ -673,11 +628,13 @@ bool StoreForwardPlusPlusModule::getNextHash(uint8_t *_root_hash, size_t _root_h
} else {
bool found_hash = false;
LOG_WARN("Looking for next hashes");
uint8_t *tmp_commit_hash;
while (sqlite3_step(getNextHashStmt) != SQLITE_DONE) {
tmp_commit_hash = (uint8_t *)sqlite3_column_blob(getNextHashStmt, 0);
if (found_hash) {
LOG_WARN("Found hash");
memcpy(next_commit_hash, tmp_commit_hash, SFPP_HASH_SIZE);
next_hash = true;
break;
@@ -693,71 +650,56 @@ bool StoreForwardPlusPlusModule::getNextHash(uint8_t *_root_hash, size_t _root_h
void StoreForwardPlusPlusModule::broadcastLink(uint8_t *_commit_hash, size_t _commit_hash_len)
{
int rc;
sqlite3_bind_int(getLinkStmt, 1, _commit_hash_len);
sqlite3_bind_blob(getLinkStmt, 2, _commit_hash, _commit_hash_len, NULL);
int res = sqlite3_step(getLinkStmt);
link_object lo;
LOG_WARN("%d", sqlite3_step(getLinkStmt));
lo.to = sqlite3_column_int(getLinkStmt, 0);
lo.from = sqlite3_column_int(getLinkStmt, 1);
lo.id = sqlite3_column_int(getLinkStmt, 2);
meshtastic_StoreForwardPlusPlus storeforward = meshtastic_StoreForwardPlusPlus_init_zero;
storeforward.sfpp_message_type = meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE;
storeforward.encapsulated_to = sqlite3_column_int(getLinkStmt, 0);
storeforward.encapsulated_from = sqlite3_column_int(getLinkStmt, 1);
storeforward.encapsulated_id = sqlite3_column_int(getLinkStmt, 2);
uint8_t *_payload = (uint8_t *)sqlite3_column_blob(getLinkStmt, 3);
lo.encrypted_len = sqlite3_column_bytes(getLinkStmt, 3);
memcpy(lo.encrypted_bytes, _payload, lo.encrypted_len);
storeforward.message.size = sqlite3_column_bytes(getLinkStmt, 3);
memcpy(storeforward.message.bytes, _payload, storeforward.message.size);
uint8_t *_message_hash = (uint8_t *)sqlite3_column_blob(getLinkStmt, 4);
lo.message_hash_len = SFPP_HASH_SIZE;
memcpy(lo.message_hash, _message_hash, lo.message_hash_len);
// storeforward.message_hash.size = 8;
// memcpy(storeforward.message_hash.bytes, _message_hash, storeforward.message_hash.size);
lo.rx_time = sqlite3_column_int(getLinkStmt, 5);
storeforward.encapsulated_rxtime = sqlite3_column_int(getLinkStmt, 5);
uint8_t *_tmp_commit_hash = (uint8_t *)sqlite3_column_blob(getLinkStmt, 6);
lo.commit_hash_len = 8;
memcpy(lo.commit_hash, _tmp_commit_hash, lo.commit_hash_len);
storeforward.commit_hash.size = 8;
memcpy(storeforward.commit_hash.bytes, _tmp_commit_hash, storeforward.commit_hash.size);
uint8_t *_root_hash = (uint8_t *)sqlite3_column_blob(getLinkStmt, 7);
lo.root_hash_len = 8;
memcpy(lo.root_hash, _root_hash, lo.root_hash_len);
storeforward.root_hash.size = 8;
memcpy(storeforward.root_hash.bytes, _root_hash, storeforward.root_hash.size);
sqlite3_reset(getLinkStmt);
meshtastic_MeshPacket *p = allocDataProtobuf(storeforward);
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
p->channel = 0;
p->hop_limit = portduino_config.sfpp_hops;
p->hop_start = portduino_config.sfpp_hops;
LOG_INFO("Send link to mesh");
broadcastLink(lo, false);
service->sendToMesh(p, RX_SRC_LOCAL, true);
}
// TODO if stratum0, send the chain count
void StoreForwardPlusPlusModule::broadcastLink(link_object &lo, bool full_commit_hash, bool is_split_second_half)
void StoreForwardPlusPlusModule::broadcastLink(link_object &lo, bool full_commit_hash)
{
meshtastic_StoreForwardPlusPlus storeforward = meshtastic_StoreForwardPlusPlus_init_zero;
storeforward.sfpp_message_type = meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE;
if (lo.encrypted_len > 180) {
LOG_WARN("Link too large to send (%u bytes)", lo.encrypted_len);
doing_split_send = true;
storeforward.message_hash.size = SFPP_SHORT_HASH_SIZE;
memcpy(storeforward.message_hash.bytes, lo.message_hash, storeforward.message_hash.size);
link_object full_link = lo;
split_link_out = lo;
size_t half_size = lo.encrypted_len / 2;
storeforward.sfpp_message_type = meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_FIRSTHALF;
lo.encrypted_len = half_size;
split_link_out.encrypted_len = full_link.encrypted_len - half_size;
memcpy(split_link_out.encrypted_bytes, &full_link.encrypted_bytes[half_size], split_link_out.encrypted_len);
setIntervalFromNow(30 * 1000); // send second half in 30 seconds
} else if (is_split_second_half) {
storeforward.sfpp_message_type = meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF;
storeforward.message_hash.size = SFPP_SHORT_HASH_SIZE;
memcpy(storeforward.message_hash.bytes, lo.message_hash, storeforward.message_hash.size);
doing_split_send = false;
}
storeforward.encapsulated_to = lo.to;
if (storeforward.encapsulated_to == NODENUM_BROADCAST) {
storeforward.encapsulated_to = 0;
}
storeforward.encapsulated_from = lo.from;
storeforward.encapsulated_id = lo.id;
@@ -772,11 +714,11 @@ void StoreForwardPlusPlusModule::broadcastLink(link_object &lo, bool full_commit
if (full_commit_hash)
storeforward.commit_hash.size = lo.commit_hash_len;
else
storeforward.commit_hash.size = SFPP_SHORT_HASH_SIZE;
storeforward.commit_hash.size = 8;
memcpy(storeforward.commit_hash.bytes, lo.commit_hash, storeforward.commit_hash.size);
}
storeforward.root_hash.size = SFPP_SHORT_HASH_SIZE;
storeforward.root_hash.size = 8;
memcpy(storeforward.root_hash.bytes, lo.root_hash, storeforward.root_hash.size);
sqlite3_reset(getLinkStmt);
@@ -796,9 +738,10 @@ void StoreForwardPlusPlusModule::broadcastLink(link_object &lo, bool full_commit
StoreForwardPlusPlusModule::link_object StoreForwardPlusPlusModule::getLink(uint8_t *_commit_hash, size_t _commit_hash_len)
{
link_object lo;
int rc;
sqlite3_bind_int(getLinkStmt, 1, _commit_hash_len);
sqlite3_bind_blob(getLinkStmt, 2, _commit_hash, _commit_hash_len, NULL);
int res = sqlite3_step(getLinkStmt);
LOG_WARN("%d", sqlite3_step(getLinkStmt));
lo.to = sqlite3_column_int(getLinkStmt, 0);
lo.from = sqlite3_column_int(getLinkStmt, 1);
@@ -835,38 +778,54 @@ StoreForwardPlusPlusModule::link_object StoreForwardPlusPlusModule::getLink(uint
bool StoreForwardPlusPlusModule::sendFromScratch(uint8_t *root_hash)
{
link_object lo;
LOG_WARN("sendFromScratch");
// "select destination, sender, packet_id, channel_hash, encrypted_bytes, message_hash, rx_time \
// from local_messages order by rx_time desc LIMIT 1;"
sqlite3_bind_blob(fromScratchStmt, 1, root_hash, SFPP_HASH_SIZE, NULL);
if (sqlite3_step(fromScratchStmt) == SQLITE_DONE) {
LOG_WARN("No messages in scratch to forward");
return false;
}
lo.to = sqlite3_column_int(fromScratchStmt, 0);
lo.from = sqlite3_column_int(fromScratchStmt, 1);
lo.id = sqlite3_column_int(fromScratchStmt, 2);
meshtastic_StoreForwardPlusPlus storeforward = meshtastic_StoreForwardPlusPlus_init_zero;
storeforward.sfpp_message_type = meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE;
storeforward.encapsulated_to = sqlite3_column_int(fromScratchStmt, 0);
storeforward.encapsulated_from = sqlite3_column_int(fromScratchStmt, 1);
storeforward.encapsulated_id = sqlite3_column_int(fromScratchStmt, 2);
uint8_t *_encrypted = (uint8_t *)sqlite3_column_blob(fromScratchStmt, 3);
lo.encrypted_len = sqlite3_column_bytes(fromScratchStmt, 3);
memcpy(lo.encrypted_bytes, _encrypted, lo.encrypted_len);
storeforward.message.size = sqlite3_column_bytes(fromScratchStmt, 3);
memcpy(storeforward.message.bytes, _encrypted, storeforward.message.size);
uint8_t *_message_hash = (uint8_t *)sqlite3_column_blob(fromScratchStmt, 4);
// storeforward.message_hash.size = SFPP_HASH_SIZE;
// memcpy(storeforward.message_hash.bytes, _message_hash, storeforward.message_hash.size);
lo.rx_time = sqlite3_column_int(fromScratchStmt, 5);
storeforward.encapsulated_rxtime = sqlite3_column_int(fromScratchStmt, 5);
lo.root_hash_len = SFPP_SHORT_HASH_SIZE;
memcpy(lo.root_hash, root_hash, lo.root_hash_len);
storeforward.root_hash.size = SFPP_SHORT_HASH_SIZE;
memcpy(storeforward.root_hash.bytes, root_hash, SFPP_SHORT_HASH_SIZE);
sqlite3_reset(fromScratchStmt);
meshtastic_MeshPacket *p = allocDataProtobuf(storeforward);
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
p->channel = 0;
p->hop_limit = portduino_config.sfpp_hops;
p->hop_start = portduino_config.sfpp_hops;
printBytes("Send link to mesh ", _message_hash, 8);
LOG_WARN("Size: %d", lo.encrypted_len);
printBytes("encrypted ", lo.encrypted_bytes, lo.encrypted_len);
broadcastLink(lo, false);
LOG_WARN("Size: %d", storeforward.message.size);
printBytes("encrypted ", storeforward.message.bytes, storeforward.message.size);
service->sendToMesh(p, RX_SRC_LOCAL, true);
return true;
}
bool StoreForwardPlusPlusModule::addToChain(link_object &lo)
{
LOG_WARN("Add to chain");
link_object chain_end = getLinkFromCount(0, lo.root_hash, lo.root_hash_len);
// we may need to calculate the full commit hash at this point
@@ -884,6 +843,7 @@ bool StoreForwardPlusPlusModule::addToChain(link_object &lo)
}
commit_hash.update(lo.message_hash, SFPP_HASH_SIZE);
// message_hash.update(&mp.rx_time, sizeof(mp.rx_time));
commit_hash.finalize(lo.commit_hash, SFPP_HASH_SIZE);
}
lo.counter = chain_end.counter + 1;
@@ -936,12 +896,9 @@ bool StoreForwardPlusPlusModule::addToScratch(link_object &lo)
sqlite3_bind_int(scratch_insert_stmt, 7, lo.rx_time);
// payload
sqlite3_bind_text(scratch_insert_stmt, 8, lo.payload.c_str(), lo.payload.length(), NULL);
const char *_error_mesg = sqlite3_errmsg(ppDb);
int res = sqlite3_step(scratch_insert_stmt);
if (res != SQLITE_OK) {
const char *_error_mesg = sqlite3_errmsg(ppDb);
LOG_WARN("step %u, %s", res, _error_mesg);
}
LOG_WARN("step %u, %s", sqlite3_step(scratch_insert_stmt), _error_mesg);
sqlite3_reset(scratch_insert_stmt);
return true;
}
@@ -949,6 +906,7 @@ bool StoreForwardPlusPlusModule::addToScratch(link_object &lo)
void StoreForwardPlusPlusModule::canonAnnounce(uint8_t *_message_hash, uint8_t *_commit_hash, uint8_t *_root_hash,
uint32_t _rx_time)
{
LOG_WARN("canonAnnounce()");
meshtastic_StoreForwardPlusPlus storeforward = meshtastic_StoreForwardPlusPlus_init_zero;
storeforward.sfpp_message_type = meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE;
// set root hash
@@ -975,7 +933,7 @@ void StoreForwardPlusPlusModule::canonAnnounce(uint8_t *_message_hash, uint8_t *
p->channel = 0;
p->hop_limit = portduino_config.sfpp_hops;
p->hop_start = portduino_config.sfpp_hops;
LOG_INFO("Send packet to mesh payload size %u", p->decoded.payload.size);
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
}
@@ -993,6 +951,7 @@ bool StoreForwardPlusPlusModule::isInDB(uint8_t *message_hash_bytes, size_t mess
bool StoreForwardPlusPlusModule::isInScratch(uint8_t *message_hash_bytes, size_t message_hash_len)
{
LOG_WARN("isInScratch");
sqlite3_bind_int(checkScratch, 1, message_hash_len);
sqlite3_bind_blob(checkScratch, 2, message_hash_bytes, message_hash_len, NULL);
sqlite3_step(checkScratch);
@@ -1005,10 +964,13 @@ bool StoreForwardPlusPlusModule::isInScratch(uint8_t *message_hash_bytes, size_t
void StoreForwardPlusPlusModule::removeFromScratch(uint8_t *message_hash_bytes, size_t message_hash_len)
{
LOG_WARN("removeFromScratch");
printBytes("removing from scratch: ", message_hash_bytes, message_hash_len);
sqlite3_bind_int(removeScratch, 1, message_hash_len);
sqlite3_bind_blob(removeScratch, 2, message_hash_bytes, message_hash_len, NULL);
sqlite3_step(removeScratch);
int numberFound = sqlite3_column_int(removeScratch, 0);
LOG_WARN("removed %d entries from scratch", numberFound);
sqlite3_reset(removeScratch);
}
@@ -1019,24 +981,23 @@ void StoreForwardPlusPlusModule::updatePayload(uint8_t *message_hash_bytes, size
sqlite3_bind_int(updatePayloadStmt, 2, message_hash_len);
sqlite3_bind_blob(updatePayloadStmt, 3, message_hash_bytes, message_hash_len, NULL);
auto res = sqlite3_step(updatePayloadStmt);
if (res != SQLITE_OK) {
const char *_error_mesg = sqlite3_errmsg(ppDb);
LOG_WARN("step error %u, %s", res, _error_mesg);
}
const char *_error_mesg = sqlite3_errmsg(ppDb);
LOG_WARN("step %u, %s", res, _error_mesg);
sqlite3_reset(updatePayloadStmt);
}
StoreForwardPlusPlusModule::link_object StoreForwardPlusPlusModule::getFromScratch(uint8_t *message_hash_bytes, size_t hash_len)
{
// vscode wrote this
LOG_WARN("getFromScratch");
link_object lo;
sqlite3_bind_int(fromScratchByHashStmt, 1, hash_len);
sqlite3_bind_blob(fromScratchByHashStmt, 2, message_hash_bytes, hash_len, NULL);
auto res = sqlite3_step(fromScratchByHashStmt);
if (res != SQLITE_ROW && res != SQLITE_OK) {
const char *_error_mesg = sqlite3_errmsg(ppDb);
LOG_WARN("step error %u, %s", res, _error_mesg);
}
const char *_error_mesg = sqlite3_errmsg(ppDb);
LOG_WARN("step %u, %s", res, _error_mesg);
lo.to = sqlite3_column_int(fromScratchByHashStmt, 0);
lo.from = sqlite3_column_int(fromScratchByHashStmt, 1);
lo.id = sqlite3_column_int(fromScratchByHashStmt, 2);
@@ -1061,6 +1022,7 @@ StoreForwardPlusPlusModule::link_object StoreForwardPlusPlusModule::getFromScrat
StoreForwardPlusPlusModule::link_object
StoreForwardPlusPlusModule::ingestTextPacket(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket *encrypted_meshpacket)
{
LOG_WARN("ingestTextPacket()");
link_object lo;
SHA256 message_hash;
lo.to = mp.to;
@@ -1084,16 +1046,12 @@ StoreForwardPlusPlusModule::ingestTextPacket(const meshtastic_MeshPacket &mp, co
return lo;
}
StoreForwardPlusPlusModule::link_object StoreForwardPlusPlusModule::ingestLinkMessage(meshtastic_StoreForwardPlusPlus *t,
bool recalc)
StoreForwardPlusPlusModule::link_object StoreForwardPlusPlusModule::ingestLinkMessage(meshtastic_StoreForwardPlusPlus *t)
{
// TODO: If not stratum0, injest the chain count
link_object lo;
lo.to = t->encapsulated_to;
if (lo.to == 0) {
lo.to = NODENUM_BROADCAST;
}
lo.from = t->encapsulated_from;
lo.id = t->encapsulated_id;
lo.rx_time = t->encapsulated_rxtime;
@@ -1104,60 +1062,52 @@ StoreForwardPlusPlusModule::link_object StoreForwardPlusPlusModule::ingestLinkMe
memcpy(lo.encrypted_bytes, t->message.bytes, t->message.size);
lo.encrypted_len = t->message.size;
if (recalc) {
message_hash.reset();
message_hash.update(lo.encrypted_bytes, lo.encrypted_len);
message_hash.update(&lo.to, sizeof(lo.to));
message_hash.update(&lo.from, sizeof(lo.from));
message_hash.update(&lo.id, sizeof(lo.id));
message_hash.finalize(lo.message_hash, SFPP_HASH_SIZE);
lo.message_hash_len = SFPP_HASH_SIZE;
// look up full root hash and copy over the partial if it matches
if (lookUpFullRootHash(t->root_hash.bytes, t->root_hash.size, lo.root_hash)) {
printBytes("Found full root hash: 0x", lo.root_hash, SFPP_HASH_SIZE);
lo.root_hash_len = SFPP_HASH_SIZE;
} else {
LOG_WARN("root hash does not match %d bytes", t->root_hash.size);
lo.root_hash_len = 0;
lo.validObject = false;
return lo;
}
message_hash.reset();
message_hash.update(lo.encrypted_bytes, lo.encrypted_len);
message_hash.update(&lo.to, sizeof(lo.to));
message_hash.update(&lo.from, sizeof(lo.from));
message_hash.update(&lo.id, sizeof(lo.id));
message_hash.finalize(lo.message_hash, SFPP_HASH_SIZE);
lo.message_hash_len = SFPP_HASH_SIZE;
if (t->commit_hash.size == SFPP_HASH_SIZE && getChainCount(t->root_hash.bytes, t->root_hash.size) == 0 &&
portduino_config.sfpp_initial_sync != 0 && !portduino_config.sfpp_stratum0) {
lo.commit_hash_len = SFPP_HASH_SIZE;
memcpy(lo.commit_hash, t->commit_hash.bytes, SFPP_HASH_SIZE);
} else if (t->commit_hash.size > 0) {
// calculate the full commit hash and replace the partial if it matches
if (checkCommitHash(lo, t->commit_hash.bytes, t->commit_hash.size)) {
printBytes("commit hash matches: 0x", t->commit_hash.bytes, t->commit_hash.size);
} else {
LOG_WARN("commit hash does not match, rejecting link.");
lo.commit_hash_len = 0;
lo.validObject = false;
}
}
// look up full root hash and copy over the partial if it matches
if (lookUpFullRootHash(t->root_hash.bytes, t->root_hash.size, lo.root_hash)) {
printBytes("Found full root hash: 0x", lo.root_hash, SFPP_HASH_SIZE);
lo.root_hash_len = SFPP_HASH_SIZE;
} else {
memcpy(lo.message_hash, t->message_hash.bytes, t->message_hash.size);
lo.message_hash_len = t->message_hash.size;
memcpy(lo.root_hash, t->root_hash.bytes, t->root_hash.size);
lo.root_hash_len = t->root_hash.size;
memcpy(lo.commit_hash, t->commit_hash.bytes, t->commit_hash.size);
lo.commit_hash_len = t->commit_hash.size;
LOG_WARN("root hash does not match %d bytes", t->root_hash.size);
lo.root_hash_len = 0;
lo.validObject = false;
return lo;
}
if (t->commit_hash.size == SFPP_HASH_SIZE && getChainCount(t->root_hash.bytes, t->root_hash.size) == 0 &&
portduino_config.sfpp_initial_sync != 0 && !portduino_config.sfpp_stratum0) {
LOG_WARN("Accepting SF++ ch ");
lo.commit_hash_len = SFPP_HASH_SIZE;
memcpy(lo.commit_hash, t->commit_hash.bytes, SFPP_HASH_SIZE);
} else if (t->commit_hash.size > 0) {
// calculate the full commit hash and replace the partial if it matches
if (checkCommitHash(lo, t->commit_hash.bytes, t->commit_hash.size)) {
printBytes("commit hash matches: 0x", t->commit_hash.bytes, t->commit_hash.size);
} else {
LOG_WARN("commit hash does not match");
lo.commit_hash_len = 0;
lo.validObject = false;
}
}
// we don't ever get the payload here, so it's always an empty string
lo.payload = "";
lo.validObject = true;
return lo;
}
void StoreForwardPlusPlusModule::rebroadcastLinkObject(link_object &lo)
{
LOG_INFO("Attempting to Rebroadcast a message received over SF++");
LOG_WARN("Attempting to Rebroadcast1");
meshtastic_MeshPacket *p = router->allocForSending();
p->to = lo.to;
p->from = lo.from;
@@ -1186,7 +1136,7 @@ bool StoreForwardPlusPlusModule::checkCommitHash(StoreForwardPlusPlusModule::lin
commit_hash.update(chain_end.commit_hash, SFPP_HASH_SIZE);
} else {
if (lo.root_hash_len != SFPP_HASH_SIZE) {
LOG_ERROR("Short root hash in link object, cannot create new chain");
LOG_WARN("Short root hash in link object, cannot create new chain");
return false;
}
printBytes("new chain root: 0x", lo.root_hash, SFPP_HASH_SIZE);
@@ -1206,13 +1156,14 @@ bool StoreForwardPlusPlusModule::checkCommitHash(StoreForwardPlusPlusModule::lin
bool StoreForwardPlusPlusModule::lookUpFullRootHash(uint8_t *partial_root_hash, size_t partial_root_hash_len,
uint8_t *full_root_hash)
{
LOG_WARN("lookUpFullRootHash");
printBytes("partial_root_hash", partial_root_hash, partial_root_hash_len);
sqlite3_bind_int(getFullRootHashStmt, 1, partial_root_hash_len);
sqlite3_bind_blob(getFullRootHashStmt, 2, partial_root_hash, partial_root_hash_len, NULL);
sqlite3_step(getFullRootHashStmt);
uint8_t *tmp_root_hash = (uint8_t *)sqlite3_column_blob(getFullRootHashStmt, 0);
if (tmp_root_hash) {
LOG_DEBUG("Found full root hash!");
LOG_WARN("Found full root hash!");
memcpy(full_root_hash, tmp_root_hash, SFPP_HASH_SIZE);
sqlite3_reset(getFullRootHashStmt);
return true;
@@ -1223,22 +1174,16 @@ bool StoreForwardPlusPlusModule::lookUpFullRootHash(uint8_t *partial_root_hash,
void StoreForwardPlusPlusModule::setChainCount(uint8_t *root_hash, size_t root_hash_len, uint32_t count)
{
sqlite3_bind_int(setChainCountStmt, 1, root_hash_len);
sqlite3_bind_blob(setChainCountStmt, 2, root_hash, root_hash_len, NULL);
sqlite3_bind_int(setChainCountStmt, 3, count);
sqlite3_bind_blob(setChainCountStmt, 1, root_hash, root_hash_len, NULL);
sqlite3_bind_int(setChainCountStmt, 2, count);
sqlite3_step(setChainCountStmt);
sqlite3_reset(setChainCountStmt);
}
uint32_t StoreForwardPlusPlusModule::getChainCount(uint8_t *root_hash, size_t root_hash_len)
{
sqlite3_bind_int(getChainCountStmt, 1, root_hash_len);
sqlite3_bind_blob(getChainCountStmt, 2, root_hash, root_hash_len, NULL);
int res = sqlite3_step(getChainCountStmt);
if (res != SQLITE_OK && res != SQLITE_DONE && res != SQLITE_ROW) {
LOG_ERROR("getChainCount sqlite error %u, %s", res, sqlite3_errmsg(ppDb));
}
sqlite3_bind_blob(getChainCountStmt, 1, root_hash, root_hash_len, NULL);
sqlite3_step(getChainCountStmt);
uint32_t count = sqlite3_column_int(getChainCountStmt, 0);
sqlite3_reset(getChainCountStmt);
return count;
@@ -1247,7 +1192,11 @@ uint32_t StoreForwardPlusPlusModule::getChainCount(uint8_t *root_hash, size_t ro
StoreForwardPlusPlusModule::link_object StoreForwardPlusPlusModule::getLinkFromCount(uint32_t _count, uint8_t *_root_hash,
size_t _root_hash_len)
{
// TODO return blank if there's no entries
LOG_WARN("getLinkFromCount");
link_object lo;
int rc;
int step = 0;
uint32_t _rx_time = 0;
@@ -1269,6 +1218,7 @@ StoreForwardPlusPlusModule::link_object StoreForwardPlusPlusModule::getLinkFromC
step++;
}
LOG_WARN("step %d", step);
if (last_message_commit_hash != nullptr && _rx_time != 0) {
lo = getLink(last_message_commit_hash, SFPP_HASH_SIZE);
} else {
@@ -1280,25 +1230,4 @@ StoreForwardPlusPlusModule::link_object StoreForwardPlusPlusModule::getLinkFromC
return lo;
}
void StoreForwardPlusPlusModule::pruneScratchQueue()
{
sqlite3_bind_int(pruneScratchQueueStmt, 1, time(nullptr) - 60 * 60 * 24);
int res = sqlite3_step(pruneScratchQueueStmt);
if (res != SQLITE_OK && res != SQLITE_DONE) {
LOG_ERROR("Prune Scratch sqlite error %u, %s", res, sqlite3_errmsg(ppDb));
}
sqlite3_reset(pruneScratchQueueStmt);
}
void StoreForwardPlusPlusModule::trimOldestLink(uint8_t *root_hash, size_t root_hash_len)
{
sqlite3_bind_int(trimOldestLinkStmt, 1, root_hash_len);
sqlite3_bind_blob(trimOldestLinkStmt, 2, root_hash, root_hash_len, NULL);
int res = sqlite3_step(trimOldestLinkStmt);
if (res != SQLITE_OK && res != SQLITE_DONE) {
LOG_ERROR("Trim Oldest Link sqlite error %u, %s", res, sqlite3_errmsg(ppDb));
}
sqlite3_reset(trimOldestLinkStmt);
}
#endif // has include sqlite3

View File

@@ -7,7 +7,7 @@
#include "SinglePortModule.h"
#include "sqlite3.h"
#define SFPP_HASH_SIZE 16
#define SFPP_HASH_SIZE 32
#define SFPP_SHORT_HASH_SIZE 8
/**
@@ -78,11 +78,11 @@ class StoreForwardPlusPlusModule : public ProtobufModule<meshtastic_StoreForward
*/
virtual bool wantPacket(const meshtastic_MeshPacket *p) override
{
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP ||
p->decoded.portnum == (portduino_config.sfpp_steal_port ? meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP
: meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP)) {
switch (p->decoded.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP:
case meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP:
return true;
} else {
default:
return false;
}
}
@@ -117,8 +117,6 @@ class StoreForwardPlusPlusModule : public ProtobufModule<meshtastic_StoreForward
sqlite3_stmt *getFullRootHashStmt;
sqlite3_stmt *setChainCountStmt;
sqlite3_stmt *getChainCountStmt;
sqlite3_stmt *pruneScratchQueueStmt;
sqlite3_stmt *trimOldestLinkStmt;
// For a given Meshtastic ChannelHash, fills the root_hash buffer with a 32-byte root hash
// returns true if the root hash was found
@@ -153,7 +151,7 @@ class StoreForwardPlusPlusModule : public ProtobufModule<meshtastic_StoreForward
void broadcastLink(uint8_t *, size_t);
// sends a LINK_PROVIDE message broadcasting the given link object
void broadcastLink(link_object &, bool, bool = false);
void broadcastLink(link_object &, bool);
// sends a LINK_PROVIDE message broadcasting the given link object from scratch message store
bool sendFromScratch(uint8_t *);
@@ -189,7 +187,7 @@ class StoreForwardPlusPlusModule : public ProtobufModule<meshtastic_StoreForward
// ingests a LINK_PROVIDE message and builds a link_object
// confirms the root hash and commit hash
link_object ingestLinkMessage(meshtastic_StoreForwardPlusPlus *, bool = true);
link_object ingestLinkMessage(meshtastic_StoreForwardPlusPlus *);
// retrieves a link object from the canonical chain database given a message hash
link_object getLink(uint8_t *, size_t);
@@ -212,10 +210,6 @@ class StoreForwardPlusPlusModule : public ProtobufModule<meshtastic_StoreForward
link_object getLinkFromCount(uint32_t, uint8_t *, size_t);
void pruneScratchQueue();
void trimOldestLink(uint8_t *, size_t);
// Track if we have a scheduled runOnce pending
// useful to not accudentally delay a scheduled runOnce
bool pendingRun = false;
@@ -226,10 +220,5 @@ class StoreForwardPlusPlusModule : public ProtobufModule<meshtastic_StoreForward
};
uint32_t rebroadcastTimeout = 3600; // Messages older than this (in seconds) will not be rebroadcast
bool doing_split_send = false;
link_object split_link_out;
bool doing_split_receive = false;
link_object split_link_in;
};
#endif

View File

@@ -429,9 +429,7 @@ int32_t PositionModule::runOnce()
if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) {
if (waitingForFreshPosition) {
#ifdef GPS_DEBUG
LOG_DEBUG("Skip initial position send; no fresh position since boot");
#endif
} else if (nodeDB->hasValidPosition(node)) {
lastGpsSend = now;

View File

@@ -69,7 +69,7 @@ class PositionModule : public ProtobufModule<meshtastic_Position>, private concu
// In event mode we want to prevent excessive position broadcasts
// we set the minimum interval to 5m
const uint32_t minimumTimeThreshold =
max(uint32_t(300000), Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30));
max(300000, Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30));
#else
const uint32_t minimumTimeThreshold =
Default::getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30);

View File

@@ -20,7 +20,7 @@ int StatusLEDModule::handleStatusUpdate(const meshtastic::Status *arg)
switch (arg->getStatusType()) {
case STATUS_TYPE_POWER: {
meshtastic::PowerStatus *powerStatus = (meshtastic::PowerStatus *)arg;
if (powerStatus->getHasUSB() || powerStatus->getIsCharging()) {
if (powerStatus->getHasUSB()) {
power_state = charging;
if (powerStatus->getBatteryChargePercent() >= 100) {
power_state = charged;

View File

@@ -21,17 +21,18 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp
// We only store/display messages destined for us.
devicestate.rx_text_message = mp;
devicestate.has_rx_text_message = true;
IF_SCREEN(
// Guard against running in MeshtasticUI or with no screen
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
// Store in the central message history
const StoredMessage &sm = messageStore.addFromPacket(mp);
#if HAS_SCREEN
// Guard against running in MeshtasticUI
if (screen && config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
// Store in the central message history
const StoredMessage &sm = messageStore.addFromPacket(mp);
// Pass message to renderer (banner + thread switching + scroll reset)
// Use the global Screen singleton to retrieve the current OLED display
auto *display = screen ? screen->getDisplayDevice() : nullptr;
graphics::MessageRenderer::handleNewMessage(display, sm, mp);
})
// Pass message to renderer (banner + thread switching + scroll reset)
// Use the global Screen singleton to retrieve the current OLED display
auto *display = screen ? screen->getDisplayDevice() : nullptr;
graphics::MessageRenderer::handleNewMessage(display, sm, mp);
}
#endif
// Only trigger screen wake if configuration allows it
if (shouldWakeOnReceivedMessage()) {
powerFSM.trigger(EVENT_RECEIVED_MSG);

View File

@@ -279,7 +279,7 @@ void portduinoSetup()
// RAK6421-13300-S1:aabbcc123456:5ba85807d92138b7519cfb60460573af:3061e8d8
// <model string>:mac address :<16 random unique bytes in hexidecimal> : crc32
// crc32 is calculated on the eeprom string up to but not including the final colon
if (strlen(autoconf_product) < 6 && portduino_config.i2cdev != "") {
if (strlen(autoconf_product) < 6) {
try {
char *mac_start = nullptr;
char *devID_start = nullptr;
@@ -790,13 +790,8 @@ bool loadConfig(const char *configPath)
if (yamlConfig["StoreAndForward"]) {
portduino_config.sfpp_stratum0 = (yamlConfig["StoreAndForward"]["Stratum0"]).as<bool>(false);
portduino_config.sfpp_enabled = (yamlConfig["StoreAndForward"]["Enabled"]).as<bool>(true);
portduino_config.sfpp_db_path = (yamlConfig["StoreAndForward"]["DBPath"]).as<std::string>("/var/lib/meshtasticd/");
portduino_config.sfpp_initial_sync = (yamlConfig["StoreAndForward"]["InitialSync"]).as<int>(10);
portduino_config.sfpp_hops = (yamlConfig["StoreAndForward"]["Hops"]).as<int>(3);
portduino_config.sfpp_announce_interval = (yamlConfig["StoreAndForward"]["AnnounceInterval"]).as<int>(5);
portduino_config.sfpp_max_chain = (yamlConfig["StoreAndForward"]["MaxChain"]).as<uint32_t>(1000);
portduino_config.sfpp_steal_port = (yamlConfig["StoreAndForward"]["StealPort"]).as<bool>(false);
}
if (yamlConfig["General"]) {
@@ -880,4 +875,4 @@ void readGPIOFromYaml(YAML::Node sourceNode, pinMapping &destPin, int pinDefault
destPin.line = destPin.pin;
destPin.gpiochip = portduino_config.lora_default_gpiochip;
}
}
}

View File

@@ -170,10 +170,9 @@ extern struct portduino_config_struct {
bool has_configDisplayMode = false;
// Store and Forward++
// DB location /var/lib/meshtasticd/
std::string sfpp_db_path = "/var/lib/meshtasticd/";
bool sfpp_stratum0 = false;
bool sfpp_enabled = true;
bool sfpp_steal_port = false;
int sfpp_initial_sync = 10;
int sfpp_hops = 3;
int sfpp_announce_interval = 5; // minutes
@@ -503,16 +502,14 @@ extern struct portduino_config_struct {
}
// StoreAndForward
if (sfpp_enabled) {
if (sfpp_stratum0 || sfpp_initial_sync != 10 || sfpp_hops != 3 || sfpp_announce_interval != 5 || sfpp_max_chain != 1000) {
out << YAML::Key << "StoreAndForward" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "Enabled" << YAML::Value << sfpp_enabled;
out << YAML::Key << "DBPath" << YAML::Value << sfpp_db_path;
out << YAML::Key << "Stratum0" << YAML::Value << sfpp_stratum0;
out << YAML::Key << "InitialSync" << YAML::Value << sfpp_initial_sync;
out << YAML::Key << "Hops" << YAML::Value << sfpp_hops;
out << YAML::Key << "AnnounceInterval" << YAML::Value << sfpp_announce_interval;
out << YAML::Key << "MaxChainLength" << YAML::Value << sfpp_max_chain;
out << YAML::Key << "StealPort" << YAML::Value << sfpp_steal_port;
out << YAML::EndMap; // StoreAndForward
}

View File

@@ -100,5 +100,3 @@
#define MODEM_DTR 8
#define MODEM_RX 10
#define MODEM_TX 11
#define HAS_PHYSICAL_KEYBOARD 1

View File

@@ -23,7 +23,6 @@
#define SCREEN_TRANSITION_FRAMERATE 5
#define BRIGHTNESS_DEFAULT 130 // Medium Low Brightness
#define USE_TFTDISPLAY 1
#define HAS_PHYSICAL_KEYBOARD 1
#define HAS_TOUCHSCREEN 1
#define SCREEN_TOUCH_INT 16

View File

@@ -21,7 +21,6 @@
#define SCREEN_TRANSITION_FRAMERATE 5
#define BRIGHTNESS_DEFAULT 130 // Medium Low Brightness
#define USE_TFTDISPLAY 1
#define HAS_PHYSICAL_KEYBOARD 1
#define I2C_SDA SDA
#define I2C_SCL SCL

View File

@@ -15,5 +15,5 @@ lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=custom.pio depName=nRF52_PWM packageName=khoih-prog/library/nRF52_PWM
khoih-prog/nRF52_PWM@1.0.1
; # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
; lewisxhe/SensorLib@0.3.3
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1

View File

@@ -114,8 +114,7 @@ extern "C" {
#define LR11X0_DIO_AS_RF_SWITCH
// PCF8563 RTC Module
// REVISIT https://github.com/meshtastic/firmware/pull/9084
// #define PCF8563_RTC 0x51
#define PCF8563_RTC 0x51
#ifdef __cplusplus
}

View File

@@ -12,5 +12,5 @@ build_flags = ${nrf52840_base.build_flags}
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/ELECROW-ThinkNode-M6>
lib_deps =
${nrf52840_base.lib_deps}
; # renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
; lewisxhe/SensorLib@0.3.3
# renovate: datasource=custom.pio depName=PCF8563 packageName=lewisxhe/library/PCF8563_Library
lewisxhe/PCF8563_Library@1.0.1

View File

@@ -41,30 +41,3 @@ void initVariant()
pinMode(VDD_FLASH_EN, OUTPUT);
digitalWrite(VDD_FLASH_EN, HIGH);
}
// called from main-nrf52.cpp during the cpuDeepSleep() function
void variant_shutdown()
{
// This sets the pin to OUTPUT and LOW for the pins *not* in the if block.
for (int pin = 0; pin < 48; pin++) {
if (pin == PIN_GPS_EN || pin == ADC_CTRL || pin == PIN_BUTTON1 || pin == PIN_SPI_MISO || pin == PIN_SPI_MOSI ||
pin == PIN_SPI_SCK) {
continue;
}
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
if (pin >= 32) {
NRF_P1->DIRCLR = (1 << (pin - 32));
} else {
NRF_GPIO->DIRCLR = (1 << pin);
}
}
digitalWrite(PIN_GPS_EN, LOW);
digitalWrite(ADC_CTRL, LOW);
// digitalWrite(RTC_POWER, LOW);
nrf_gpio_cfg_input(PIN_BUTTON1, NRF_GPIO_PIN_PULLUP); // Configure the pin to be woken up as an input
nrf_gpio_pin_sense_t sense1 = NRF_GPIO_PIN_SENSE_LOW;
nrf_gpio_cfg_sense_set(PIN_BUTTON1, sense1);
}

View File

@@ -44,10 +44,8 @@ extern "C" {
#define LED_BLUE -1
#define LED_CHARGE (12)
#define LED_PAIRING (7)
#define PIN_LED2 LED_PAIRING
#define LED_STATE_ON HIGH
#define LED_STATE_OFF LOW
#define LED_STATE_ON 1
// USB power detection
#define EXT_PWR_DETECT (13)
@@ -121,8 +119,7 @@ static const uint8_t A0 = PIN_A0;
#define PIN_SERIAL2_TX (24)
// PCF8563 RTC Module
// REVISIT https://github.com/meshtastic/firmware/pull/9084
// #define PCF8563_RTC 0x51
#define PCF8563_RTC 0x51
// SPI
#define SPI_INTERFACES_COUNT 1

View File

@@ -103,7 +103,7 @@ static const uint8_t A0 = PIN_A0;
#define EXTERNAL_FLASH_USE_QSPI
// Add a delay on startup to allow LoRa and GPS to power up
#define PERIPHERAL_WARMUP_MS 100
#define PIN_PWR_DELAY_MS 100
/*
* Lora radio
@@ -178,4 +178,4 @@ static const uint8_t A0 = PIN_A0;
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif
#endif

View File

@@ -1,4 +1,4 @@
[VERSION]
major = 2
minor = 7
build = 17
build = 18