Compare commits

..

45 Commits

Author SHA1 Message Date
Thomas Göttgens
d657177f0e Merge branch 'master' into store-and-forward 2025-07-13 18:18:19 +02:00
Thomas Göttgens
d3fc155186 Merge branch 'master' into store-and-forward 2025-06-25 20:16:49 +02:00
Thomas Göttgens
3fb4ab35f1 Merge branch 'master' into store-and-forward 2025-05-24 22:06:23 +02:00
Woutvstk
6d8bedb027 Clean up unused macros and outdated comments (#6520)
* Updating comments and removing residue

* Some more unused macros
2025-04-08 08:27:47 -05:00
Ben Meadors
aca9ceebfa Merge branch 'master' into store-and-forward 2025-04-07 08:42:41 -05:00
Ben Meadors
9dee6030e4 Merge branch 'master' into store-and-forward 2025-04-07 08:33:52 -05:00
Thomas Göttgens
0d19f766e8 fix build errors 2025-04-07 13:00:28 +02:00
Thomas Göttgens
91986db1b0 Merge branch 'master' into store-and-forward 2025-04-07 09:27:53 +02:00
Thomas Göttgens
1e54d3bfc7 Merge branch 'master' into store-and-forward 2025-03-31 11:25:28 +02:00
Ben Meadors
b2401100de Merge branch 'master' into store-and-forward 2025-03-30 20:36:00 -05:00
Woutvstk
7e7792aa51 Changed SD library for nrf52 and got S&F working (#6382)
- Changed library from deprecated arduino SD library to wrapper of SdFat library
- Fixed some bugs in the S&F code for SD card storage
Now the S&F functionality works on nrf52 (tested on rak4631 with RAK15002)
2025-03-29 13:15:21 +01:00
Ben Meadors
d237d4f311 Merge branch 'master' into store-and-forward 2025-03-28 06:17:28 -05:00
Thomas Göttgens
9f2a3fd23b Merge pull request #6132 from Woutvstk/retry-fix-wrongly-removed-spi1-rak4631 2025-03-02 13:27:10 +01:00
Thomas Göttgens
61901c37bb Merge branch 'store-and-forward' into retry-fix-wrongly-removed-spi1-rak4631 2025-03-02 13:26:14 +01:00
Thomas Göttgens
64f8203abc Merge branch 'master' into store-and-forward 2025-03-02 12:05:49 +01:00
Woutvstk
f0ca0b947c trunk fmt 2025-02-25 16:04:59 +01:00
Woutvstk
db376532ad Add SPIM3/SPIM2 for SPI/SPI1 selection
The SPIM3 is faster (max 32Mhz) than SPIM2 (max 8Mhz)
Default is SPI_32MHZ_INTERFACE  0 in SPI library
2025-02-25 15:22:03 +01:00
Woutvstk
372b62aa35 remove second initSPI() (#6140) 2025-02-24 19:45:44 +08:00
Woutvstk
95fe4aed04 trunk fmt 2025-02-23 18:17:24 +01:00
Woutvstk
b19d358dcc redid the reorganisation of the SPI definitions 2025-02-23 17:23:35 +01:00
Woutvstk
4ec0134606 Revert "Rak4631 remove spi1 (#6042)"
This reverts commit 9b46cb4ef0.
2025-02-23 16:56:39 +01:00
Woutvstk
5aa4946e6f Revert "Reorganize spi definitions for use with sd cards ESP32/NRF52 (#6080)"
This reverts commit 443922b947.
2025-02-23 16:56:22 +01:00
Thomas Göttgens
e67b84ee06 Merge branch 'master' into store-and-forward 2025-02-19 13:22:11 +01:00
Woutvstk
443922b947 Reorganize spi definitions for use with sd cards ESP32/NRF52 (#6080)
Changed variant files of hardware that uses HAS_SDCARD.
Reorganised SPIClass definitions in FSCommon
2025-02-18 09:08:03 +01:00
Thomas Göttgens
35fc93752e Merge branch 'master' into store-and-forward 2025-02-17 15:57:31 +01:00
Woutvstk
a6aa84431c added quotes against linking issues in platformio.ini issue #5898 (#5966) 2025-01-31 07:08:57 -06:00
Thomas Göttgens
9ab50d5feb Merge branch 'master' into store-and-forward 2025-01-18 14:11:59 +01:00
Thomas Göttgens
45ff66e713 Merge branch 'master' into store-and-forward 2025-01-05 23:26:32 +01:00
Thomas Göttgens
f7feea63f7 Attempt to merge 2025-01-05 21:34:38 +01:00
Thomas Göttgens
7c08ff35f3 Merge branch 'master' into store-and-forward 2025-01-05 21:20:44 +01:00
Woutvstk
96277ed804 First attempt to adding hardware support for NRF52 SPI SD Card (#5561)
* First attempt to adding hardware support for NRF52 SPI SD Card using arduino SD library
My first time contributing to an open source project so not very confident in what i'm doing.

Changes to
FSCommon:
initializing SD library for NRF52. Progress: No compile error, but SD card does not get initialized properly yet
added ifdef ARCH_ESP32 conditions around esp32 SD library functions

memget: added ifdef conditional statements

StoreForwardModule.cpp: added ifdef conditional statements

Rak4631 platfromIO.ini and variant.h:
added arduino-libraries/SD@^1.3.0 library to libdeps
defined HAS_SDCARD and SPI pins

Arduino SD library. Made changes to library because using namespace SDLIB in header file caused ambiguity problems
Not sure this is the right way of adding a library, also, how do i implement changes to the library permanently to the project?

Am I going somewhat in the right direction with these changes? Tell me your thoughts, thanks

* replaced arduino SD library to custom fork.
A "using namespace" statement in the header file was to messy to work around.
NRF52 SD card initialisation added

* updated library reference
added card size and type function to SD library
added populateSDCard for NRF52

* Changed NRF52 SD object from SDFilesystem to SD

Changed NRF52 SD object from SDFilesystem to SD for more compatibility with esp32 SD library. Some functions are still different but most used open, read, write and close are the same.

* Removed duplicate ESP32/NRF52 SD card access code

Mainly made changes to the custom arduino SD library to make it compatible with the esp32 SD library already used in the store and forward code.
With these compatible function names and return, I removed some duplicate code.

* trunk fmt and pin SD library to commit hash

* print this out on ESP32 anyway

---------

Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
2025-01-05 21:19:16 +01:00
Thomas Göttgens
37a03c2ef9 Merge branch 'master' into store-and-forward 2024-12-29 22:41:38 +01:00
Thomas Göttgens
ef602a7d81 Merge branch 'master' into store-and-forward 2024-12-12 16:40:27 +01:00
Thomas Göttgens
1f1d785ca5 Merge branch 'master' into store-and-forward 2024-12-09 13:53:33 +01:00
Thomas Göttgens
37da78aec2 Merge branch 'master' into store-and-forward 2024-11-24 15:52:25 +01:00
Thomas Göttgens
beb69e93d0 Merge branch 'master' into store-and-forward 2024-11-03 15:15:44 +01:00
Thomas Göttgens
5d25c304ad Merge branch 'master' into store-and-forward 2024-11-02 16:38:36 +01:00
Thomas Göttgens
fbe6a3c4d2 Merge branch 'master' into store-and-forward 2024-10-28 12:11:46 +01:00
Thomas Göttgens
91933c66f3 missed one. 2024-10-27 17:31:10 +01:00
Thomas Göttgens
a13a299b92 next one - fix compilation for Portduino 2024-10-26 14:39:12 +02:00
Thomas Göttgens
94155f170e fix compilation, double macros resolved 2024-10-26 13:58:29 +02:00
Thomas Göttgens
840996c755 fix build for portduino 2024-10-26 13:41:31 +02:00
Thomas Göttgens
1027b45ee9 Merge branch 'store-and-forward' of https://github.com/meshtastic/firmware into store-and-forward 2024-10-26 13:39:44 +02:00
Thomas Göttgens
7532052edc Use SD card as store&forward memory 2024-10-26 12:21:18 +02:00
Thomas Göttgens
b1125513f3 Use SD card as store&forward memory 2024-10-16 21:11:24 +02:00
74 changed files with 817 additions and 949 deletions

View File

@@ -30,11 +30,7 @@ jobs:
strategy:
fail-fast: false
matrix:
series:
- jammy # 22.04
- noble # 24.04
- plucky # 25.04
- questing # 25.10
series: [plucky, noble, jammy]
uses: ./.github/workflows/package_ppa.yml
with:
ppa_repo: ppa:meshtastic/daily

View File

@@ -20,11 +20,7 @@ jobs:
strategy:
fail-fast: false
matrix:
series:
- jammy # 22.04
- noble # 24.04
- plucky # 25.04
# - questing # 25.10
series: [plucky, noble, jammy]
uses: ./.github/workflows/package_ppa.yml
with:
ppa_repo: |-

View File

@@ -9,7 +9,7 @@ plugins:
lint:
enabled:
- checkov@3.2.450
- renovate@41.30.5
- renovate@41.29.1
- prettier@3.6.2
- trufflehog@3.89.2
- yamllint@1.37.1

View File

@@ -23,20 +23,14 @@ build_flags =
-DMESHTASTIC_EXCLUDE_SCREEN=1
-DMESHTASTIC_EXCLUDE_MQTT=1
-DMESHTASTIC_EXCLUDE_BLUETOOTH=1
-DMESHTASTIC_EXCLUDE_GPS=1
-DMESHTASTIC_EXCLUDE_WIFI=1
-DMESHTASTIC_EXCLUDE_TZ=1 ; Exclude TZ to save some flash space.
-DSERIAL_RX_BUFFER_SIZE=256 ; For GPS - the default of 64 is too small.
-DHAS_SCREEN=0 ; Always disable screen for STM32, it is not supported.
-DPIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF ; This is REQUIRED for at least traceroute debug prints - without it the length ends up uninitialized.
-DDEBUG_MUTE ; You can #undef DEBUG_MUTE in certain source files if you need the logs.
;-DDEBUG_MUTE
-fmerge-all-constants
-ffunction-sections
-fdata-sections
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
-DHAL_DAC_MODULE_ONLY
-DHAL_RNG_MODULE_ENABLED
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/RemoteHardwareModule.cpp> -<platform/nrf52> -<platform/portduino> -<platform/rp2xx0> -<mesh/raspihttp>

View File

@@ -3,7 +3,6 @@
# trunk-ignore-all(flake8/F821): For SConstruct imports
import sys
from os.path import join
import subprocess
import json
import re
@@ -93,17 +92,6 @@ prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
verObj = readProps(prefsLoc)
print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"] + " on " + env.get("PIOENV"))
# get repository owner if git is installed
try:
r_owner = (
subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
.decode("utf-8")
.strip().split("/")
)
repo_owner = r_owner[-2] + "/" + r_owner[-1].replace(".git", "")
except subprocess.CalledProcessError:
repo_owner = "unknown"
jsonLoc = env["PROJECT_DIR"] + "/userPrefs.jsonc"
with open(jsonLoc) as f:
jsonStr = re.sub("//.*","", f.read(), flags=re.MULTILINE)
@@ -129,7 +117,6 @@ flags = [
"-DAPP_VERSION=" + verObj["long"],
"-DAPP_VERSION_SHORT=" + verObj["short"],
"-DAPP_ENV=" + env.get("PIOENV"),
"-DAPP_REPO=" + repo_owner,
] + pref_flags
print ("Using flags:")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

View File

@@ -23,11 +23,16 @@ SPIClass SPI_HSPI(HSPI);
#else
#define SDHandler SPI
#endif
#elif defined(ARCH_NRF52)
#if defined(SDCARD_USE_SPI1)
#define SDHandler SPI1
#elif defined(SDCARD_USE_SPI)
#define SDHandler SPI
#endif // NRF52 SPI or SPI1
#endif // ESP32/NRF52
#ifndef SD_SPI_FREQUENCY
#define SD_SPI_FREQUENCY 4000000U
#endif
#endif // HAS_SDCARD
/**
@@ -309,7 +314,13 @@ void setupSDCard()
{
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI)
concurrency::LockGuard g(spiLock);
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52))
#if (defined(ARCH_ESP32))
SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
#elif (defined(ARCH_NRF52))
SDHandler.begin();
#endif
if (!SD.begin(SDCARD_CS, SDHandler, SD_SPI_FREQUENCY)) {
LOG_DEBUG("No SD_MMC card detected");
return;
@@ -319,20 +330,23 @@ void setupSDCard()
LOG_DEBUG("No SD_MMC card attached");
return;
}
LOG_DEBUG("SD_MMC Card Type: ");
if (cardType == CARD_MMC) {
LOG_DEBUG("MMC");
LOG_DEBUG("SD_MMC Card Type: MMC");
} else if (cardType == CARD_SD) {
LOG_DEBUG("SDSC");
LOG_DEBUG("SD_MMC Card Type: SDSC");
} else if (cardType == CARD_SDHC) {
LOG_DEBUG("SDHC");
LOG_DEBUG("SD_MMC Card Type: SDHC");
} else {
LOG_DEBUG("UNKNOWN");
LOG_DEBUG("SD_MMC Card Type: UNKNOWN");
}
uint64_t cardSize = SD.cardSize() / (1024 * 1024);
LOG_DEBUG("SD Card Size: %lu MB", (uint32_t)cardSize);
LOG_DEBUG("Total space: %lu MB", (uint32_t)(SD.totalBytes() / (1024 * 1024)));
LOG_DEBUG("Used space: %lu MB", (uint32_t)(SD.usedBytes() / (1024 * 1024)));
LOG_INFO("Now scanning free clusters on SD card, this may take some time...");
delay(100); // let serial print the above statement properly
LOG_DEBUG("Used space: %lu MB", (uint32_t)(SD.usedBytes() / (1024 * 1024))); // This might take some time during boot
#endif
#endif
}

View File

@@ -39,9 +39,9 @@ template <typename T, std::size_t N> std::size_t array_count(const T (&)[N])
return N;
}
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
#if defined(GPS_SERIAL_PORT)
HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT;
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
#if defined(RAK2560)
HardwareSerial *GPS::_serial_gps = &Serial2;
#else
HardwareSerial *GPS::_serial_gps = &Serial1;
#endif

View File

@@ -226,14 +226,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
time_t res = gm_mktime(&t);
struct timeval tv;
tv.tv_sec = res;
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
#ifdef BUILD_EPOCH
if (tv.tv_sec < BUILD_EPOCH) {
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
return RTCSetResultInvalidTime;
}
#endif
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
// LOG_DEBUG("Got time from GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec);
if (t.tm_year < 0 || t.tm_year >= 300) {

View File

@@ -1,156 +0,0 @@
#include "BRC.h"
#include "GPSStatus.h"
#include "gps/GeoCoord.h"
#include "graphics/Screen.h"
#if HAS_SCREEN
using namespace meshtastic;
const int32_t BRC_LATI = (40.786958 * 1e7);
const int32_t BRC_LONI = (-119.202994 * 1e7);
const double BRC_LATF = 40.786958;
const double BRC_LONF = -119.202994;
const double BRC_NOON = 1.5;
const double RAD_TO_HOUR = (6.0 / 3.14159);
const double METER_TO_FEET = 3.28084;
const double FEET_TO_METER = 1.0 / METER_TO_FEET;
// Pre-calculated street data for performance
struct StreetInfo {
float center;
float width;
const char *name;
};
/*
# python code to generate the StreetInfo
esp_center = 2500
street_info = [
# name, width, preceeding block depth
('Esp', 40, 60), # block size is fake
('A', 30, 400),
('B', 30, 250),
('C', 30, 250),
('D', 30, 250),
('E', 40, 250),
('F', 30, 450), # E-F block is exra deep
('G', 30, 250),
('H', 30, 250),
('I', 30, 250),
('J', 30, 150),
('K', 50, 150),
]
street_center = esp_center - street_info[0][1] //2 - street_info[0][2]
last_center = esp_center
for (name, street_width, block_width) in street_info:
offset = (street_width + block_width) // 2
street_center += street_width //2 + block_width
dia = street_center * 2
dist = street_center - last_center
print(f"{{{street_center}, {offset}, \"{name}\"}},\t// +{dist}ft\tdia: {dia:,}ft")
last_center = street_center
street_center += street_width //2
street_center += 50 # extra buffer after the edge of k to include walk-in camping parking
print(f"{{{street_center}, 0, nullptr}},\t// +{street_center-last_center}ft")
*/
static const StreetInfo streets[] = {
{2500, 50, "Esp"}, // +0ft dia: 5,000ft
{2935, 215, "A"}, // +435ft dia: 5,870ft
{3215, 140, "B"}, // +280ft dia: 6,430ft
{3495, 140, "C"}, // +280ft dia: 6,990ft
{3775, 140, "D"}, // +280ft dia: 7,550ft
{4060, 145, "E"}, // +285ft dia: 8,120ft
{4545, 240, "F"}, // +485ft dia: 9,090ft
{4825, 140, "G"}, // +280ft dia: 9,650ft
{5105, 140, "H"}, // +280ft dia: 10,210ft
{5385, 140, "I"}, // +280ft dia: 10,770ft
{5565, 90, "J"}, // +180ft dia: 11,130ft
{5755, 100, "K"}, // +190ft dia: 11,510ft
{5830, 0, nullptr}, // +75ft
};
BRCAddress::BRCAddress(int32_t lat, int32_t lon)
{
bearing = GeoCoord::bearing(BRC_LATF, BRC_LONF, DegD(lat), DegD(lon)) * RAD_TO_HOUR;
bearing += 12.0 - BRC_NOON;
while (bearing > 12.0) {
bearing -= 12.0;
}
// In imperial units because that is how golden spike data is provided.
distance = GeoCoord::latLongToMeter(BRC_LATF, BRC_LONF, DegD(lat), DegD(lon)) * METER_TO_FEET;
};
int BRCAddress::radial(char *buf, size_t len)
{
uint8_t hour = (uint8_t)(bearing);
uint8_t minute = (uint8_t)((bearing - hour) * 60.0);
hour %= 12;
if (hour == 0) {
hour = 12;
}
return snprintf(buf, len, "%d:%02d", hour, minute);
};
int BRCAddress::annular(char *buf, size_t len, bool noUnit)
{
const char *unit = "m";
float unitMultiplier = FEET_TO_METER;
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
unitMultiplier = 1.0;
unit = "ft";
}
if (noUnit)
unit = "";
if (bearing > 1.75 && bearing < 10.25) {
const char *street = nullptr;
float dist = 0;
// Find the appropriate street based on distance
for (const auto &s : streets) {
if (distance > s.center - s.width) {
street = s.name;
dist = distance - s.center;
} else {
break;
}
}
if (street) {
return snprintf(buf, len, "%s %d%s", street, int(dist * unitMultiplier), unit);
}
}
return snprintf(buf, len, "%d%s", int(distance * unitMultiplier), unit);
};
int BRCAddress::full(char *buf, size_t len)
{
auto l = radial(buf, len - 4);
buf += l;
*(buf++) = ' ';
*(buf++) = '&';
*(buf++) = ' ';
buf += annular(buf, len - l - 4, false);
buf[l] = 0; // always null terminated
return l;
};
int BRCAddress::compact(char *buf, size_t len)
{
auto l = radial(buf, len - 2);
buf += l;
*(buf++) = '&';
buf += annular(buf, len - l - 2, true);
buf[l] = 0; // always null terminated
return l;
};
#endif

View File

@@ -1,21 +0,0 @@
#pragma once
// For size_t/int32_t types on some platforms.
#include <cstdint>
// For size_t
#include <cstddef>
class BRCAddress
{
public:
BRCAddress(int32_t lat, int32_t lon);
int radial(char *buf, size_t len);
int annular(char *buf, size_t len, bool noUnit);
int full(char *buf, size_t len);
int compact(char *buf, size_t len);
private:
float bearing;
float distance;
};

View File

@@ -62,6 +62,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
#include "modules/WaypointModule.h"
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
#include "modules/StoreForwardModule.h"
#endif
#include "sleep.h"
#include "target_specific.h"
@@ -76,10 +79,13 @@ extern uint16_t TFT_MESH;
#endif
#ifdef ARCH_ESP32
<<<<<<< store-and-forward
#include "esp_task_wdt.h"
=======
>>>>>>> master
#endif
#if ARCH_PORTDUINO
#include "modules/StoreForwardModule.h"
#include "platform/portduino/PortduinoGlue.h"
#endif
@@ -386,7 +392,9 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
#ifdef T_WATCH_S3
PMU->enablePowerOutput(XPOWERS_ALDO2);
#endif
#ifdef HELTEC_TRACKER_V1_X
uint8_t tft_vext_enabled = digitalRead(VEXT_ENABLE);
#endif
#if !ARCH_PORTDUINO
dispdev->displayOn();
#endif
@@ -398,7 +406,10 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
dispdev->displayOn();
#ifdef HELTEC_TRACKER_V1_X
ui->init();
// If the TFT VEXT power is not enabled, initialize the UI.
if (!tft_vext_enabled) {
ui->init();
}
#endif
#ifdef USE_ST7789
pinMode(VTFT_CTRL, OUTPUT);
@@ -864,8 +875,6 @@ void Screen::setFrames(FrameFocus focus)
uint8_t previousFrameCount = framesetInfo.frameCount;
FramesetInfo fsi; // Location of specific frames, for applying focus parameter
graphics::UIRenderer::rebuildFavoritedNodes();
LOG_DEBUG("Show standard frames");
showingNormalScreen = true;
@@ -924,12 +933,6 @@ void Screen::setFrames(FrameFocus focus)
normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses;
indicatorIcons.push_back(icon_list);
#if HAS_SCREEN
fsi.positions.nodelist_brc = numframes;
normalFrames[numframes++] = graphics::NodeListRenderer::drawBRCList;
indicatorIcons.push_back(icon_bm);
#endif
fsi.positions.gps = numframes;
normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen;
indicatorIcons.push_back(icon_compass);
@@ -1233,6 +1236,327 @@ void Screen::setFastFramerate()
runASAP = true;
}
<<<<<<< store-and-forward
void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setFont(FONT_SMALL);
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
}
char channelStr[20];
{
concurrency::LockGuard guard(&lock);
snprintf(channelStr, sizeof(channelStr), "#%s", channels.getName(channels.getPrimaryIndex()));
}
// Display power status
if (powerStatus->getHasBattery()) {
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
drawBattery(display, x, y + 2, imgBattery, powerStatus);
} else {
drawBattery(display, x + 1, y + 3, imgBattery, powerStatus);
}
} else if (powerStatus->knowsUSB()) {
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
display->drawFastImage(x, y + 2, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
} else {
display->drawFastImage(x + 1, y + 3, 16, 8, powerStatus->getHasUSB() ? imgUSB : imgPower);
}
}
// Display nodes status
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 2, nodeStatus);
} else {
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 3, nodeStatus);
}
#if HAS_GPS
// Display GPS status
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
drawGPSpowerstat(display, x, y + 2, gpsStatus);
} else {
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
} else {
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 3, gpsStatus);
}
}
#endif
display->setColor(WHITE);
// Draw the channel name
display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr);
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
// Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo
if (moduleConfig.store_forward.enabled) {
if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat,
(storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
imgQuestionL1);
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8,
imgQuestionL2);
#else
display->drawFastImage(x + SCREEN_WIDTH - 10 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8,
imgQuestion);
#endif
} else {
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 16, 8,
imgSFL1);
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 16, 8,
imgSFL2);
#else
display->drawFastImage(x + SCREEN_WIDTH - 13 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 11, 8,
imgSF);
#endif
}
#else
// No store and forward, show a exclamation mark
if (false) {
#endif
} else {
// TODO: Raspberry Pi supports more than just the one screen size
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
imgInfoL1);
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8,
imgInfoL2);
#else
display->drawFastImage(x + SCREEN_WIDTH - 10 - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8, imgInfo);
#endif
}
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(ourId), y + FONT_HEIGHT_SMALL, ourId);
// Draw any log messages
display->drawLogBuffer(x, y + (FONT_HEIGHT_SMALL * 2));
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
if (heartbeat)
display->setPixel(0, 0);
heartbeat = !heartbeat;
#endif
}
// Jm
void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
const char *wifiName = config.network.wifi_ssid;
display->setFont(FONT_SMALL);
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
}
if (WiFi.status() != WL_CONNECTED) {
display->drawString(x, y, String("WiFi: Not Connected"));
if (config.display.heading_bold)
display->drawString(x + 1, y, String("WiFi: Not Connected"));
} else {
display->drawString(x, y, String("WiFi: Connected"));
if (config.display.heading_bold)
display->drawString(x + 1, y, String("WiFi: Connected"));
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())), y,
"RSSI " + String(WiFi.RSSI()));
if (config.display.heading_bold) {
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("RSSI " + String(WiFi.RSSI())) - 1, y,
"RSSI " + String(WiFi.RSSI()));
}
}
display->setColor(WHITE);
/*
- WL_CONNECTED: assigned when connected to a WiFi network;
- WL_NO_SSID_AVAIL: assigned when no SSID are available;
- WL_CONNECT_FAILED: assigned when the connection fails for all the attempts;
- WL_CONNECTION_LOST: assigned when the connection is lost;
- WL_DISCONNECTED: assigned when disconnected from a network;
- WL_IDLE_STATUS: it is a temporary status assigned when WiFi.begin() is called and remains active until the number of
attempts expires (resulting in WL_CONNECT_FAILED) or a connection is established (resulting in WL_CONNECTED);
- WL_SCAN_COMPLETED: assigned when the scan networks is completed;
- WL_NO_SHIELD: assigned when no WiFi shield is present;
*/
if (WiFi.status() == WL_CONNECTED) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.localIP().toString().c_str()));
} else if (WiFi.status() == WL_NO_SSID_AVAIL) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "SSID Not Found");
} else if (WiFi.status() == WL_CONNECTION_LOST) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Lost");
} else if (WiFi.status() == WL_CONNECT_FAILED) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Failed");
} else if (WiFi.status() == WL_IDLE_STATUS) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Idle ... Reconnecting");
}
#ifdef ARCH_ESP32
else {
// Codes:
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code
display->drawString(x, y + FONT_HEIGHT_SMALL * 1,
WiFi.disconnectReasonName(static_cast<wifi_err_reason_t>(getWifiDisconnectReason())));
}
#else
else {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Unkown status: " + String(WiFi.status()));
}
#endif
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName));
display->drawString(x, y + FONT_HEIGHT_SMALL * 3, "http://meshtastic.local");
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
if (heartbeat)
display->setPixel(0, 0);
heartbeat = !heartbeat;
#endif
#endif
}
void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setFont(FONT_SMALL);
// The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT);
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
}
char batStr[20];
if (powerStatus->getHasBattery()) {
int batV = powerStatus->getBatteryVoltageMv() / 1000;
int batCv = (powerStatus->getBatteryVoltageMv() % 1000) / 10;
snprintf(batStr, sizeof(batStr), "B %01d.%02dV %3d%% %c%c", batV, batCv, powerStatus->getBatteryChargePercent(),
powerStatus->getIsCharging() ? '+' : ' ', powerStatus->getHasUSB() ? 'U' : ' ');
// Line 1
display->drawString(x, y, batStr);
if (config.display.heading_bold)
display->drawString(x + 1, y, batStr);
} else {
// Line 1
display->drawString(x, y, String("USB"));
if (config.display.heading_bold)
display->drawString(x + 1, y, String("USB"));
}
// auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true);
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
// if (config.display.heading_bold)
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode);
uint32_t currentMillis = millis();
uint32_t seconds = currentMillis / 1000;
uint32_t minutes = seconds / 60;
uint32_t hours = minutes / 60;
uint32_t days = hours / 24;
// currentMillis %= 1000;
// seconds %= 60;
// minutes %= 60;
// hours %= 24;
// Show uptime as days, hours, minutes OR seconds
std::string uptime = screen->drawTimeDelta(days, hours, minutes, seconds);
// Line 1 (Still)
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
if (config.display.heading_bold)
display->drawString(x - 1 + SCREEN_WIDTH - display->getStringWidth(uptime.c_str()), y, uptime.c_str());
display->setColor(WHITE);
// Setup string to assemble analogClock string
std::string analogClock = "";
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
// hms += tz.tz_dsttime * SEC_PER_HOUR;
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
// Tear apart hms into h:m:s
int hour = hms / SEC_PER_HOUR;
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
char timebuf[12];
if (config.display.use_12h_clock) {
std::string meridiem = "am";
if (hour >= 12) {
if (hour > 12)
hour -= 12;
meridiem = "pm";
}
if (hour == 00) {
hour = 12;
}
snprintf(timebuf, sizeof(timebuf), "%d:%02d:%02d%s", hour, min, sec, meridiem.c_str());
} else {
snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d", hour, min, sec);
}
analogClock += timebuf;
}
// Line 2
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, analogClock.c_str());
// Display Channel Utilization
char chUtil[13];
snprintf(chUtil, sizeof(chUtil), "ChUtil %2.0f%%", airTime->channelUtilizationPercent());
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil), y + FONT_HEIGHT_SMALL * 1, chUtil);
#if HAS_GPS
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
// Line 3
if (config.display.gps_format !=
meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DMS) // if DMS then don't draw altitude
drawGPSAltitude(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
// Line 4
drawGPScoordinates(display, x, y + FONT_HEIGHT_SMALL * 3, gpsStatus);
} else {
drawGPSpowerstat(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
}
#endif
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
if (heartbeat)
display->setPixel(0, 0);
heartbeat = !heartbeat;
#endif
}
=======
>>>>>>> master
int Screen::handleStatusUpdate(const meshtastic::Status *arg)
{
// LOG_DEBUG("Screen got status update %d", arg->getStatusType());
@@ -1382,12 +1706,9 @@ int Screen::handleInputEvent(const InputEvent *event)
menuHandler::clockMenu();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) {
menuHandler::LoraRegionPicker();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) {
if (devicestate.rx_text_message.from) {
menuHandler::messageResponseMenu();
} else {
menuHandler::textMessageBaseMenu();
}
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage &&
devicestate.rx_text_message.from) {
menuHandler::messageResponseMenu();
} else if (framesetInfo.positions.firstFavorite != 255 &&
this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite &&
this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) {
@@ -1397,8 +1718,7 @@ int Screen::handleInputEvent(const InputEvent *event)
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal ||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_distance ||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal ||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_bearings ||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_brc) {
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_bearings) {
menuHandler::nodeListMenu();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.wifi) {
menuHandler::wifiBaseMenu();

View File

@@ -662,7 +662,6 @@ class Screen : public concurrency::OSThread
uint8_t nodelist_hopsignal = 255;
uint8_t nodelist_distance = 255;
uint8_t nodelist_bearings = 255;
uint8_t nodelist_brc = 255;
uint8_t clock = 255;
uint8_t firstFavorite = 255;
uint8_t lastFavorite = 255;

View File

@@ -206,7 +206,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
timeX = screenW - xOffset - timeStrWidth + 3;
// === Show Mail or Mute Icon to the Left of Time ===
int iconRightEdge = timeX - 2;
int iconRightEdge = timeX - 1;
bool showMail = false;

View File

@@ -186,7 +186,7 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
{
display->clear();
display->setTextAlignment(TEXT_ALIGN_LEFT);
int line = 1;
// === Set Title, Blank for Clock
const char *titleStr = "";
// === Header ===
@@ -230,8 +230,6 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
#ifdef T_WATCH_S3
float scale = 1.5;
#elif defined(CHATTER_2)
float scale = 1.1;
#else
float scale = 0.75;
if (isHighResolution) {
@@ -287,9 +285,6 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
int yOffset = (isHighResolution) ? 3 : 1;
#ifdef SENSECAP_INDICATOR
yOffset -= 3;
#endif
#ifdef T_DECK
yOffset -= 5;
#endif
if (config.display.use_12h_clock) {
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,

View File

@@ -483,7 +483,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
}
// ****************************
// * System Screen *
// * Memory Screen *
// ****************************
void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -593,19 +593,7 @@ void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
}
line += 1;
char appversionstr[35];
snprintf(appversionstr, sizeof(appversionstr), "Ver: %s", optstr(APP_VERSION));
char appversionstr_formatted[40];
char *lastDot = strrchr(appversionstr, '.');
if (lastDot) {
size_t prefixLen = lastDot - appversionstr;
strncpy(appversionstr_formatted, appversionstr, prefixLen);
appversionstr_formatted[prefixLen] = '\0';
strncat(appversionstr_formatted, " (", sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1);
strncat(appversionstr_formatted, lastDot + 1, sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1);
strncat(appversionstr_formatted, ")", sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1);
strncpy(appversionstr, appversionstr_formatted, sizeof(appversionstr) - 1);
appversionstr[sizeof(appversionstr) - 1] = '\0';
}
snprintf(appversionstr, sizeof(appversionstr), "Ver.: %s", optstr(APP_VERSION));
int textWidth = display->getStringWidth(appversionstr);
int nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line], appversionstr);

View File

@@ -344,37 +344,14 @@ void menuHandler::homeBaseMenu()
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::textMessageBaseMenu()
{
enum optionsNumbers { Back, Preset, Freetext, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
optionsArray[options] = "New Preset Msg";
optionsEnumArray[options++] = Preset;
if (kb_found) {
optionsArray[options] = "New Freetext Msg";
optionsEnumArray[options++] = Freetext;
}
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Message Action";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Preset) {
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
} else if (selected == Freetext) {
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST);
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::systemBaseMenu()
{
// Check if brightness is supported
bool hasSupportBrightness = false;
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT
hasSupportBrightness = true;
#endif
enum optionsNumbers { Back, Notifications, ScreenOptions, PowerMenu, Test, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[enumEnd] = {Back};
@@ -444,11 +421,11 @@ void menuHandler::favoriteBaseMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Preset) {
if (selected == 1) {
cannedMessageModule->LaunchWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
} else if (selected == Freetext) {
} else if (selected == 2 && kb_found) {
cannedMessageModule->LaunchFreetextWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
} else if (selected == Remove) {
} else if ((!kb_found && selected == 2) || (selected == 3 && kb_found)) {
menuHandler::menuQueue = menuHandler::remove_favorite;
screen->runNow();
}
@@ -701,7 +678,6 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 10;
bannerOptions.bannerCallback = [display](int selected) -> void {
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT
uint8_t TFT_MESH_r = 0;
uint8_t TFT_MESH_g = 0;
uint8_t TFT_MESH_b = 0;
@@ -753,6 +729,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
screen->runNow();
}
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
if (selected != 0) {
display->setColor(BLACK);
display->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
@@ -850,9 +827,8 @@ void menuHandler::removeFavoriteMenu()
bannerOptions.optionsCount = 2;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) {
LOG_INFO("Removing %x as favorite node", graphics::UIRenderer::currentFavoriteNodeNum);
nodeDB->set_favorite(false, graphics::UIRenderer::currentFavoriteNodeNum);
screen->setFrames(graphics::Screen::FOCUS_DEFAULT);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
}
};
screen->showOverlayBanner(bannerOptions);
@@ -948,28 +924,23 @@ void menuHandler::screenOptionsMenu()
{
// Check if brightness is supported
bool hasSupportBrightness = false;
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT
hasSupportBrightness = true;
#endif
#if defined(T_DECK)
// TDeck Doesn't seem to support brightness at all, at least not reliably
hasSupportBrightness = false;
#endif
enum optionsNumbers { Back, Brightness, ScreenColor };
static const char *optionsArray[4] = {"Back"};
static int optionsEnumArray[4] = {Back};
int options = 1;
// Only show brightness for B&W displays
if (hasSupportBrightness) {
if (hasSupportBrightness && !HAS_TFT) {
optionsArray[options] = "Brightness";
optionsEnumArray[options++] = Brightness;
}
// Only show screen color for TFT displays
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
optionsArray[options] = "Screen Color";
optionsEnumArray[options++] = ScreenColor;
#endif

View File

@@ -48,7 +48,6 @@ class menuHandler
static void ClockFacePicker();
static void messageResponseMenu();
static void homeBaseMenu();
static void textMessageBaseMenu();
static void systemBaseMenu();
static void favoriteBaseMenu();
static void positionBaseMenu();

View File

@@ -6,7 +6,6 @@
#include "UIRenderer.h"
#include "gps/GeoCoord.h"
#include "gps/RTC.h" // for getTime() function
#include "graphics/BRC.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
#include "graphics/images.h"
@@ -88,8 +87,6 @@ const char *getCurrentModeTitle(int screenWidth)
#endif
case MODE_DISTANCE:
return "Distance";
case MODE_BEARING:
return "Bearing";
default:
return "Nodes";
}
@@ -312,9 +309,6 @@ void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
case MODE_DISTANCE:
drawNodeDistance(display, node, x, y, columnWidth);
break;
case MODE_BEARING:
drawEntryCompass(display, node, x, y, columnWidth);
break;
default:
break;
}
@@ -341,35 +335,6 @@ void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
}
}
void drawEntryBRC(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
{
bool isLeftCol = (x < SCREEN_WIDTH / 2);
// Adjust max text width depending on column and screen width
int nameMaxWidth = columnWidth - (isHighResolution ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22));
const char *nodeName = getSafeNodeName(node);
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
auto xText = x + ((isHighResolution) ? 6 : 3);
display->drawString(xText, y, nodeName);
if (nodeDB->hasValidPosition(node)) {
char buf[14] = "";
BRCAddress(node->position.latitude_i, node->position.longitude_i).compact(buf, 14);
auto nameWidth = display->getStringWidth("WWWW") - 2; // Fixed width so they are aligned.
display->drawString(xText + nameWidth, y, buf);
}
if (node->is_favorite) {
if (isHighResolution) {
drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display);
} else {
display->drawXbm(x, y + 5, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint);
}
}
}
void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth, float myHeading,
double userLat, double userLon)
{
@@ -419,46 +384,17 @@ void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
*/
}
void drawLastSeenExtra(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth,
float /*myHeading*/, double /*userLat*/, double /*userLon*/)
{
char timeStr[10];
uint32_t seconds = sinceLastSeen(node);
if (seconds == 0 || seconds == UINT32_MAX) {
snprintf(timeStr, sizeof(timeStr), "?");
} else {
uint32_t minutes = seconds / 60, hours = minutes / 60, days = hours / 24;
snprintf(timeStr, sizeof(timeStr), (days > 99 ? "?" : "%d%c"),
(days ? days
: hours ? hours
: minutes),
(days ? 'd'
: hours ? 'h'
: 'm'));
}
bool isLeftCol = (x < SCREEN_WIDTH / 2);
int timeOffset = (isHighResolution) ? (isLeftCol ? 7 : 10) : (isLeftCol ? 3 : 7);
int rightEdge = x + columnWidth - timeOffset;
if (timeStr[strlen(timeStr) - 1] == 'm') // Fix the fact that our fonts don't line up well all the time
rightEdge -= 1;
// display->setTextAlignment(TEXT_ALIGN_RIGHT);
int textWidth = display->getStringWidth(timeStr);
display->drawString(rightEdge - textWidth, y, timeStr);
}
// =============================
// Main Screen Functions
// =============================
void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *title,
EntryRenderer renderer, NodeExtrasRenderer extras, float heading, double lat, double lon,
int totalColumns)
EntryRenderer renderer, NodeExtrasRenderer extras, float heading, double lat, double lon)
{
const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
const int rowYOffset = FONT_HEIGHT_SMALL - 3;
int columnWidth = display->getWidth() / totalColumns;
int columnWidth = display->getWidth() / 2;
display->clear();
@@ -472,6 +408,7 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
int visibleNodeRows = totalRowsAvailable;
int totalColumns = 2;
int startIndex = scrollIndex * visibleNodeRows * totalColumns;
if (nodeDB->getMeshNodeByIndex(startIndex)->num == nodeDB->getNodeNum()) {
@@ -509,7 +446,7 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
}
// Draw column separator
if (shownCount > 0 && totalColumns > 1) {
if (shownCount > 0) {
const int firstNodeY = y + 3;
drawColumnSeparator(display, x, firstNodeY, lastNodeY);
}
@@ -545,31 +482,7 @@ void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state,
// Render screen based on currentMode
const char *title = getCurrentModeTitle(display->getWidth());
if (currentMode == MODE_BEARING) {
float heading = 0;
bool validHeading = false;
auto ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
double lat = DegD(ourNode->position.latitude_i);
double lon = DegD(ourNode->position.longitude_i);
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
if (screen->hasHeading()) {
heading = screen->getHeading(); // degrees
validHeading = true;
} else {
heading = screen->estimatedHeading(lat, lon);
validHeading = !isnan(heading);
}
if (!validHeading) {
lastRenderedMode = MODE_COUNT;
return;
}
}
drawNodeListScreen(display, state, x, y, title, drawEntryCompass, drawCompassArrow, heading, lat, lon);
} else {
drawNodeListScreen(display, state, x, y, title, drawEntryDynamic);
}
drawNodeListScreen(display, state, x, y, title, drawEntryDynamic);
// Track the last mode to avoid reinitializing modeStartTime
lastRenderedMode = currentMode;
@@ -626,11 +539,6 @@ void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state,
drawNodeListScreen(display, state, x, y, "Bearings", drawEntryCompass, drawCompassArrow, heading, lat, lon);
}
void drawBRCList(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
drawNodeListScreen(display, state, x, y, "BRC", drawEntryBRC, drawLastSeenExtra, 0, 0, 0, 1);
}
/// Draw a series of fields in a column, wrapping to multiple columns if needed
void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
{
@@ -656,4 +564,4 @@ void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields
} // namespace NodeListRenderer
} // namespace graphics
#endif
#endif

View File

@@ -24,12 +24,12 @@ typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t,
typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int, float, double, double);
// Node list mode enumeration
enum NodeListMode { MODE_LAST_HEARD = 0, MODE_HOP_SIGNAL = 1, MODE_DISTANCE = 2, MODE_BEARING = 3, MODE_COUNT = 4 };
enum NodeListMode { MODE_LAST_HEARD = 0, MODE_HOP_SIGNAL = 1, MODE_DISTANCE = 2, MODE_COUNT = 3 };
// Main node list screen function
void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *title,
EntryRenderer renderer, NodeExtrasRenderer extras = nullptr, float heading = 0, double lat = 0,
double lon = 0, int totalColumns = 2);
double lon = 0);
// Entry renderers
void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
@@ -48,7 +48,6 @@ void drawHopSignalScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
void drawDistanceScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
void drawBRCList(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
// Utility functions
const char *getCurrentModeTitle(int screenWidth);

View File

@@ -156,7 +156,7 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS
resetBanner();
return;
}
if (curSelected == static_cast<int8_t>(numDigits)) {
if (curSelected == numDigits) {
alertBannerCallback(currentNumber);
resetBanner();
return;

View File

@@ -8,7 +8,6 @@
#include "airtime.h"
#include "configuration.h"
#include "gps/GeoCoord.h"
#include "graphics/BRC.h"
#include "graphics/Screen.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
@@ -25,23 +24,6 @@ extern graphics::Screen *screen;
namespace graphics
{
NodeNum UIRenderer::currentFavoriteNodeNum = 0;
std::vector<meshtastic_NodeInfoLite *> graphics::UIRenderer::favoritedNodes;
void graphics::UIRenderer::rebuildFavoritedNodes()
{
favoritedNodes.clear();
size_t total = nodeDB->getNumMeshNodes();
for (size_t i = 0; i < total; i++) {
meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
if (!n || n->num == nodeDB->getNodeNum())
continue;
if (n->is_favorite)
favoritedNodes.push_back(n);
}
std::sort(favoritedNodes.begin(), favoritedNodes.end(),
[](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; });
}
#if !MESHTASTIC_EXCLUDE_GPS
// GeoCoord object for coordinate conversions
@@ -219,7 +201,27 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
// **********************
void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// --- Cache favorite nodes for the current frame only, to save computation ---
static std::vector<meshtastic_NodeInfoLite *> favoritedNodes;
static int prevFrame = -1;
// --- Only rebuild favorites list if we're on a new frame ---
if (state->currentFrame != prevFrame) {
prevFrame = state->currentFrame;
favoritedNodes.clear();
size_t total = nodeDB->getNumMeshNodes();
for (size_t i = 0; i < total; i++) {
meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
// Skip nulls and ourself
if (!n || n->num == nodeDB->getNodeNum())
continue;
if (n->is_favorite)
favoritedNodes.push_back(n);
}
// Keep a stable, consistent display order
std::sort(favoritedNodes.begin(), favoritedNodes.end(),
[](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; });
}
if (favoritedNodes.empty())
return;
@@ -309,12 +311,23 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
display->drawString(x, getTextPositions(display)[line++], seenStr);
}
// === 4. Burning Man location (only show if their position is known) ===
char brcStr[32] = "";
if (nodeDB->hasValidPosition(node) && line < 5) {
brcStr[0] = 32; // Space before the address to align with other rows.
BRCAddress(node->position.latitude_i, node->position.longitude_i).full(brcStr + 1, sizeof(brcStr) - 1);
display->drawString(x, getTextPositions(display)[line++], brcStr);
// === 4. Uptime (only show if metric is present) ===
char uptimeStr[32] = "";
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
uint32_t uptime = node->device_metrics.uptime_seconds;
uint32_t days = uptime / 86400;
uint32_t hours = (uptime % 86400) / 3600;
uint32_t mins = (uptime % 3600) / 60;
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
if (days)
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %ud %uh", days, hours);
else if (hours)
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %uh %um", hours, mins);
else
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
}
if (uptimeStr[0] && line < 5) {
display->drawString(x, getTextPositions(display)[line++], uptimeStr);
}
// === 5. Distance (only if both nodes have GPS position) ===
@@ -644,7 +657,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
char combinedName[50];
snprintf(combinedName, sizeof(combinedName), "%s (%s)", longNameStr.empty() ? "" : longNameStr.c_str(), shortnameble);
if (SCREEN_WIDTH - (display->getStringWidth(combinedName)) > 10) {
if (SCREEN_WIDTH - (display->getStringWidth(longName) + display->getStringWidth(shortnameble)) > 10) {
size_t len = strlen(combinedName);
if (len >= 3 && strcmp(combinedName + len - 3, " ()") == 0) {
combinedName[len - 3] = '\0'; // Remove the last three characters
@@ -655,7 +668,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
nameX, ((rows == 4) ? getTextPositions(display)[line++] : getTextPositions(display)[line++]) + yOffset, combinedName);
} else {
// === LongName Centered ===
textWidth = display->getStringWidth(longNameStr.c_str());
textWidth = display->getStringWidth(longName);
nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line++], longNameStr.c_str());
@@ -911,7 +924,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
}
// If GPS is off, no need to display these parts
if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0 && gpsStatus->getHasLock()) {
if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) {
// === Second Row: Date ===
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
@@ -932,18 +945,14 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
snprintf(lonStr, sizeof(lonStr), " Lon: %.5f", geoCoord.getLongitude() * 1e-7);
display->drawString(x, getTextPositions(display)[line++], lonStr);
// === Fifth Row: Burning Man! ===
char addrStr[32];
BRCAddress(geoCoord.getLatitude(), geoCoord.getLongitude()).full(addrStr, sizeof(addrStr));
display->drawString(x, getTextPositions(display)[line++], addrStr);
} else {
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
// from cell phone?
if (nodeDB->hasValidPosition(ourNode)) {
char addrStr[32];
BRCAddress(ourNode->position.latitude_i, ourNode->position.longitude_i).full(addrStr, sizeof(addrStr));
display->drawString(x, getTextPositions(display)[line++], addrStr);
// === Fifth Row: Altitude ===
char DisplayLineTwo[32] = {0};
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), " Alt: %.0fft", geoCoord.getAltitude() * METERS_TO_FEET);
} else {
snprintf(DisplayLineTwo, sizeof(DisplayLineTwo), " Alt: %.0im", geoCoord.getAltitude());
}
display->drawString(x, getTextPositions(display)[line++], DisplayLineTwo);
}
// === Draw Compass if heading is valid ===
@@ -1219,4 +1228,4 @@ std::string UIRenderer::drawTimeDelta(uint32_t days, uint32_t hours, uint32_t mi
} // namespace graphics
#endif // HAS_SCREEN
#endif // HAS_SCREEN

View File

@@ -61,8 +61,6 @@ class UIRenderer
static void drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
static NodeNum currentFavoriteNodeNum;
static std::vector<meshtastic_NodeInfoLite *> favoritedNodes;
static void rebuildFavoritedNodes();
// OEM screens
#ifdef USERPREFS_OEM_TEXT

View File

@@ -156,18 +156,6 @@ const uint8_t icon_list[] PROGMEM = {
0x82 // Row 7: #.....#.
};
// ➤ The Man Icon (8x8)
const uint8_t icon_bm[] PROGMEM = {
0x42, // Row 0: .#....#.
0x3C, // Row 1: ..####..
0x3C, // Row 2: ..####..
0x18, // Row 3: ...##...
0x18, // Row 4: ...##...
0x24, // Row 5: ..#..#..
0x24, // Row 6: ..#..#..
0x42 // Row 7: .#....#.
};
// 📶 Signal Bars Icon (left to right, small to large with spacing)
const uint8_t icon_signal[] PROGMEM = {
0b00000000, // ░░░░░░░

View File

@@ -67,5 +67,4 @@ void CardKbI2cImpl::init()
}
#endif
inputBroker->registerSource(this);
kb_found = true;
}

View File

@@ -286,7 +286,7 @@ void lateInitVariant() {}
*/
void printInfo()
{
LOG_INFO("S:B:%d,%s,%s,%s", HW_VENDOR, optstr(APP_VERSION), optstr(APP_ENV), optstr(APP_REPO));
LOG_INFO("S:B:%d,%s", HW_VENDOR, optstr(APP_VERSION));
}
#ifndef PIO_UNIT_TESTING
void setup()
@@ -515,11 +515,25 @@ void setup()
LOG_INFO("Scan for i2c devices");
#endif
#if defined(I2C_SDA1) || (defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2))
#if defined(I2C_SDA1) && defined(ARCH_RP2040)
Wire1.setSDA(I2C_SDA1);
Wire1.setSCL(I2C_SCL1);
Wire1.begin();
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1);
#elif defined(I2C_SDA1) && !defined(ARCH_RP2040)
Wire1.begin(I2C_SDA1, I2C_SCL1);
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1);
#elif defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2)
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1);
#endif
#if defined(I2C_SDA)
#if defined(I2C_SDA) && defined(ARCH_RP2040)
Wire.setSDA(I2C_SDA);
Wire.setSCL(I2C_SCL);
Wire.begin();
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE);
#elif defined(I2C_SDA) && !defined(ARCH_RP2040)
Wire.begin(I2C_SDA, I2C_SCL);
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE);
#elif defined(ARCH_PORTDUINO)
if (settingsStrings[i2cdev] != "") {
@@ -686,6 +700,8 @@ void setup()
i2cScanner.reset();
#endif
// initSPI() must have called at this point (must be before screen and lora)
#ifdef HAS_SDCARD
setupSDCard();
#endif
@@ -778,7 +794,6 @@ void setup()
drv.setMode(DRV2605_MODE_INTTRIG);
#endif
// Init our SPI controller (must be before screen and lora)
#ifdef ARCH_RP2040
#ifdef HW_SPI1_DEVICE
SPI1.setSCK(LORA_SCK);

View File

@@ -10,10 +10,6 @@
#include "memGet.h"
#include "configuration.h"
#ifdef ARCH_STM32WL
#include <malloc.h>
#endif
MemGet memGet;
/**
@@ -28,9 +24,6 @@ uint32_t MemGet::getFreeHeap()
return dbgHeapFree();
#elif defined(ARCH_RP2040)
return rp2040.getFreeHeap();
#elif defined(ARCH_STM32WL)
struct mallinfo m = mallinfo();
return m.fordblks; // Total free space (bytes)
#else
// this platform does not have heap management function implemented
return UINT32_MAX;
@@ -49,9 +42,6 @@ uint32_t MemGet::getHeapSize()
return dbgHeapTotal();
#elif defined(ARCH_RP2040)
return rp2040.getTotalHeap();
#elif defined(ARCH_STM32WL)
struct mallinfo m = mallinfo();
return m.arena; // Non-mmapped space allocated (bytes)
#else
// this platform does not have heap management function implemented
return UINT32_MAX;
@@ -67,6 +57,8 @@ uint32_t MemGet::getFreePsram()
{
#ifdef ARCH_ESP32
return ESP.getFreePsram();
#elif (defined(HAS_SDCARD) && defined(ARCH_ESP32))
return SD.totalBytes() - SD.usedBytes();
#elif defined(ARCH_PORTDUINO)
return 4194252;
#else
@@ -83,6 +75,8 @@ uint32_t MemGet::getPsramSize()
{
#ifdef ARCH_ESP32
return ESP.getPsramSize();
#elif (defined(HAS_SDCARD) && defined(ARCH_ESP32))
return SD.totalBytes();
#elif defined(ARCH_PORTDUINO)
return 4194252;
#else

View File

@@ -72,11 +72,10 @@ void FloodingRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p)
tosend->hop_limit--; // bump down the hop count
#if USERPREFS_EVENT_MODE
// BURNING MESH!
if (tosend->hop_limit > 3) {
if (tosend->hop_limit > 2) {
// if we are "correcting" the hop_limit, "correct" the hop_start by the same amount to preserve hops away.
tosend->hop_start -= (tosend->hop_limit - 3);
tosend->hop_limit = 3;
tosend->hop_start -= (tosend->hop_limit - 2);
tosend->hop_limit = 2;
}
#endif
tosend->next_hop = NO_NEXT_HOP_PREFERENCE; // this should already be the case, but just in case

View File

@@ -283,7 +283,6 @@ void MeshService::sendToPhone(meshtastic_MeshPacket *p)
{
perhapsDecode(p);
#ifdef ARCH_ESP32
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
if (moduleConfig.store_forward.enabled && storeForwardModule->isServer() &&
p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) {
@@ -291,7 +290,6 @@ void MeshService::sendToPhone(meshtastic_MeshPacket *p)
fromNum++; // Notify observers for packet from radio
return;
}
#endif
#endif
if (toPhoneQueue.numFree() == 0) {

View File

@@ -13,11 +13,9 @@
#if defined(ARCH_PORTDUINO)
#include "../platform/portduino/SimRadio.h"
#endif
#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
#include "modules/StoreForwardModule.h"
#endif
#endif
extern Allocator<meshtastic_QueueStatus> &queueStatusPool;
extern Allocator<meshtastic_MqttClientProxyMessage> &mqttClientProxyMessagePool;

View File

@@ -38,8 +38,7 @@ enum RxSource {
#define HOP_MAX 7
/// We normally just use max 3 hops for sending reliable messages
// BURNING MESH!
#define HOP_RELIABLE 4
#define HOP_RELIABLE 3
// For old firmware or when falling back to flooding, there is no next-hop preference
#define NO_NEXT_HOP_PREFERENCE 0

View File

@@ -22,6 +22,9 @@
#include "mesh-pb-constants.h"
#include "meshUtils.h"
#include "modules/NeighborInfoModule.h"
// #if !MESHTASTIC_EXCLUDE_STOREFORWARD
// #include "modules/StoreForwardModule.h"
// #endif
#include <ErriezCRC32.h>
#include <algorithm>
#include <pb_decode.h>
@@ -32,8 +35,6 @@
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
#include "SPILock.h"
#include "modules/StoreForwardModule.h"
#include <Preferences.h>
#include <esp_efuse.h>
#include <esp_efuse_table.h>
@@ -42,8 +43,10 @@
#include <soc/soc.h>
#endif
#ifdef ARCH_PORTDUINO
#include "SPILock.h"
#include "modules/StoreForwardModule.h"
#ifdef ARCH_PORTDUINO
#include "platform/portduino/PortduinoGlue.h"
#endif

View File

@@ -185,7 +185,7 @@ bool RF95Interface::init()
#endif
if (res == RADIOLIB_ERR_NONE)
res = lora->setCRC(RADIOLIB_SX126X_LORA_CRC_ON);
res = lora->setCRC(true);
if (res == RADIOLIB_ERR_NONE)
startReceive(); // start receiving

View File

@@ -7,9 +7,9 @@
#include "meshtastic/channel.pb.h"
#include "meshtastic/config.pb.h"
#include "meshtastic/connection_status.pb.h"
#include "meshtastic/device_ui.pb.h"
#include "meshtastic/mesh.pb.h"
#include "meshtastic/module_config.pb.h"
#include "meshtastic/device_ui.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.

View File

@@ -102,11 +102,7 @@ typedef enum _meshtastic_Config_DeviceConfig_BuzzerMode {
meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY = 2,
/* Non-notification system buzzer tones only.
Buzzer is enabled only for non-notification tones such as button presses, startup, shutdown, but not for alerts. */
meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY = 3,
/* Direct Message notifications only.
Buzzer is enabled only for direct messages and alerts, but not for button presses.
External notification config determines the specifics of the notification behavior. */
meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY = 4
meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY = 3
} meshtastic_Config_DeviceConfig_BuzzerMode;
/* Bit field of boolean configuration options, indicating which optional
@@ -649,8 +645,8 @@ extern "C" {
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_RebroadcastMode)(meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY+1))
#define _meshtastic_Config_DeviceConfig_BuzzerMode_MIN meshtastic_Config_DeviceConfig_BuzzerMode_ALL_ENABLED
#define _meshtastic_Config_DeviceConfig_BuzzerMode_MAX meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY
#define _meshtastic_Config_DeviceConfig_BuzzerMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_BuzzerMode)(meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY+1))
#define _meshtastic_Config_DeviceConfig_BuzzerMode_MAX meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY
#define _meshtastic_Config_DeviceConfig_BuzzerMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_BuzzerMode)(meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY+1))
#define _meshtastic_Config_PositionConfig_PositionFlags_MIN meshtastic_Config_PositionConfig_PositionFlags_UNSET
#define _meshtastic_Config_PositionConfig_PositionFlags_MAX meshtastic_Config_PositionConfig_PositionFlags_SPEED

View File

@@ -6,10 +6,10 @@
#include <pb.h>
#include <vector>
#include "meshtastic/channel.pb.h"
#include "meshtastic/config.pb.h"
#include "meshtastic/localonly.pb.h"
#include "meshtastic/mesh.pb.h"
#include "meshtastic/telemetry.pb.h"
#include "meshtastic/config.pb.h"
#include "meshtastic/localonly.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.

View File

@@ -6,11 +6,11 @@
#include <pb.h>
#include "meshtastic/channel.pb.h"
#include "meshtastic/config.pb.h"
#include "meshtastic/device_ui.pb.h"
#include "meshtastic/module_config.pb.h"
#include "meshtastic/portnums.pb.h"
#include "meshtastic/telemetry.pb.h"
#include "meshtastic/xmodem.pb.h"
#include "meshtastic/device_ui.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
@@ -247,26 +247,32 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_NOMADSTAR_METEOR_PRO = 96,
/* Elecrow CrowPanel Advance models, ESP32-S3 and TFT with SX1262 radio plugin */
meshtastic_HardwareModel_CROWPANEL = 97,
/* Lilygo LINK32 board with sensors */
/* *
Lilygo LINK32 board with sensors */
meshtastic_HardwareModel_LINK_32 = 98,
/* Seeed Tracker L1 */
/* *
Seeed Tracker L1 */
meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1 = 99,
/* Seeed Tracker L1 EINK driver */
/* *
Seeed Tracker L1 EINK driver */
meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1_EINK = 100,
/* Reserved ID for future and past use */
meshtastic_HardwareModel_QWANTZ_TINY_ARMS = 101,
/* Lilygo T-Deck Pro */
/* *
Lilygo T-Deck Pro */
meshtastic_HardwareModel_T_DECK_PRO = 102,
/* Lilygo TLora Pager */
/* *
Lilygo TLora Pager */
meshtastic_HardwareModel_T_LORA_PAGER = 103,
/* GAT562 Mesh Trial Tracker */
/* *
GAT562 Mesh Trial Tracker */
meshtastic_HardwareModel_GAT562_MESH_TRIAL_TRACKER = 104,
/* RAKwireless WisMesh Tag */
/* *
RAKwireless WisMesh Tag */
meshtastic_HardwareModel_WISMESH_TAG = 105,
/* RAKwireless WisBlock Core RAK3312 https://docs.rakwireless.com/product-categories/wisduo/rak3112-module/overview/ */
/* *
RAKwireless WisBlock Core RAK3312 https://docs.rakwireless.com/product-categories/wisduo/rak3112-module/overview/ */
meshtastic_HardwareModel_RAK3312 = 106,
/* Elecrow ThinkNode M5 https://www.elecrow.com/wiki/ThinkNode_M5_Meshtastic_LoRa_Signal_Transceiver_ESP32-S3.html */
meshtastic_HardwareModel_THINKNODE_M5 = 107,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */

View File

@@ -11,7 +11,7 @@
/* Enum definitions */
/* Any significant power changing event in meshtastic should be tagged with a powermon state transition.
If you are making new meshtastic features feel free to add new entries at the end of this definition. */
If you are making new meshtastic features feel free to add new entries at the end of this definition. */
typedef enum _meshtastic_PowerMon_State {
meshtastic_PowerMon_State_None = 0,
meshtastic_PowerMon_State_CPU_DeepSleep = 1,
@@ -34,13 +34,13 @@ something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of
meshtastic_PowerMon_State_Screen_Drawing = 512,
meshtastic_PowerMon_State_Wifi_On = 1024,
/* GPS is actively trying to find our location
See GPSPowerState for more details */
See GPSPowerState for more details */
meshtastic_PowerMon_State_GPS_Active = 2048
} meshtastic_PowerMon_State;
/* What operation would we like the UUT to perform.
note: senders should probably set want_response in their request packets, so that they can know when the state
machine has started processing their request */
note: senders should probably set want_response in their request packets, so that they can know when the state
machine has started processing their request */
typedef enum _meshtastic_PowerStressMessage_Opcode {
/* Unset/unused */
meshtastic_PowerStressMessage_Opcode_UNSET = 0,
@@ -67,7 +67,7 @@ typedef enum _meshtastic_PowerStressMessage_Opcode {
/* Struct definitions */
/* Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs).
But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */
But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */
typedef struct _meshtastic_PowerMon {
char dummy_field;
} meshtastic_PowerMon;

View File

@@ -850,13 +850,7 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
this->waitingForAck = true;
// Log outgoing message
LOG_INFO("Send message id=%u, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
if (p->to != 0xffffffff) {
LOG_INFO("Proactively adding %x as favorite node", p->to);
nodeDB->set_favorite(true, p->to);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
}
LOG_INFO("Send message id=%d, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
// Send to mesh and phone (even if no phone connected, to track ACKs)
service->sendToMesh(p, RX_SRC_LOCAL, true);

View File

@@ -75,7 +75,6 @@
#if !MESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE
#include "modules/GenericThreadModule.h"
#endif
#ifdef ARCH_ESP32
#if defined(USE_SX1280) && !MESHTASTIC_EXCLUDE_AUDIO
#include "modules/esp32/AudioModule.h"
@@ -83,9 +82,6 @@
#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
#include "modules/esp32/PaxcounterModule.h"
#endif
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
#include "modules/StoreForwardModule.h"
#endif
#endif
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO)
#if !MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION
@@ -253,7 +249,7 @@ void setupModules()
paxcounterModule = new PaxcounterModule();
#endif
#endif
#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(HAS_SDCARD)
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
storeForwardModule = new StoreForwardModule();
#endif

View File

@@ -14,11 +14,6 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
{
auto p = *pptr;
if (p.is_licensed != owner.is_licensed) {
LOG_WARN("Invalid nodeInfo detected, is_licensed mismatch!");
return true;
}
// Coerce user.id to be derived from the node number
snprintf(p.id, sizeof(p.id), "!%08x", getFrom(&mp));

View File

@@ -266,11 +266,9 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket()
LOG_INFO("Position packet: time=%i lat=%i lon=%i", p.time, p.latitude_i, p.longitude_i);
#ifndef MESHTASTIC_EXCLUDE_ATAK
// TAK Tracker devices should send their position in a TAK packet over the ATAK port
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)
return allocAtakPli();
#endif
return allocDataProtobuf(p);
}

View File

@@ -32,7 +32,7 @@ StoreForwardModule *storeForwardModule;
int32_t StoreForwardModule::runOnce()
{
#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(HAS_SDCARD)
if (moduleConfig.store_forward.enabled && is_server) {
// Send out the message queue.
if (this->busy) {
@@ -89,6 +89,32 @@ void StoreForwardModule::populatePSRAM()
LOG_DEBUG("After PSRAM init: heap %d/%d PSRAM %d/%d", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(),
memGet.getPsramSize());
LOG_DEBUG("numberOfPackets for packetHistory - %u", numberOfPackets);
this->storageType = StorageType::ST_PSRAM;
}
/**
* if we have an SDCARD, format it for store&forward use
*/
void StoreForwardModule::populateSDCard()
{
#if defined(HAS_SDCARD)
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52))
spiLock->lock();
if (SD.cardType() != CARD_NONE) {
if (!SD.exists("/storeforward")) {
LOG_INFO("Creating StoreForward directory");
SD.mkdir("/storeforward");
}
this->storageType = StorageType::ST_SDCARD;
uint32_t numberOfPackets = (this->records ? this->records : (((SD.totalBytes() / 3) * 2) / sizeof(PacketHistoryStruct)));
this->records = numberOfPackets;
// only allocate space for one temp copy
this->packetHistory = (PacketHistoryStruct *)malloc(sizeof(PacketHistoryStruct));
LOG_DEBUG("numberOfPackets for packetHistory - %u", numberOfPackets);
}
spiLock->unlock();
#endif // ARCH_ESP32 || ARCH_NRF52
#endif // HAS_SDCARD
}
/**
@@ -135,14 +161,42 @@ uint32_t StoreForwardModule::getNumAvailablePackets(NodeNum dest, uint32_t last_
lastRequest.emplace(dest, 0);
}
for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) {
if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) {
// Client is only interested in packets not from itself and only in broadcast packets or packets towards it.
if (this->packetHistory[i].from != dest &&
(this->packetHistory[i].to == NODENUM_BROADCAST || this->packetHistory[i].to == dest)) {
count++;
if (this->storageType == StorageType::ST_PSRAM) {
if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) {
// Client is only interested in packets not from itself and only in broadcast packets or packets towards it.
if (this->packetHistory[i].from != dest &&
(this->packetHistory[i].to == NODENUM_BROADCAST || this->packetHistory[i].to == dest)) {
count++;
}
}
} else if (this->storageType == StorageType::ST_SDCARD) {
#if defined(HAS_SDCARD)
#if defined(ARCH_ESP32) || defined(ARCH_NRF52)
spiLock->lock();
auto handler = SD.open("/storeforward/" + String(i), FILE_READ);
if (handler) {
if (handler.read((uint8_t *)&this->packetHistory[0], sizeof(PacketHistoryStruct)) !=
sizeof(PacketHistoryStruct)) {
LOG_ERROR("SD card reading error");
}
handler.close();
if (this->packetHistory[0].time && (this->packetHistory[0].time > last_time)) {
// Client is only interested in packets not from itself and only in broadcast packets or packets towards it.
if (this->packetHistory[0].from != dest &&
(this->packetHistory[0].to == NODENUM_BROADCAST || this->packetHistory[0].to == dest)) {
count++;
}
}
}
spiLock->unlock();
#endif
#endif
} else {
LOG_ERROR("S&F: Unknown storage type");
}
}
return count;
}
@@ -187,23 +241,47 @@ void StoreForwardModule::historyAdd(const meshtastic_MeshPacket &mp)
const auto &p = mp.decoded;
if (this->packetHistoryTotalCount == this->records) {
LOG_WARN("S&F - PSRAM Full. Starting overwrite");
LOG_WARN("S&F - Storage Full. Starting overwrite");
this->packetHistoryTotalCount = 0;
for (auto &i : lastRequest) {
i.second = 0; // Clear the last request index for each client device
}
}
this->packetHistory[this->packetHistoryTotalCount].time = getTime();
this->packetHistory[this->packetHistoryTotalCount].to = mp.to;
this->packetHistory[this->packetHistoryTotalCount].channel = mp.channel;
this->packetHistory[this->packetHistoryTotalCount].from = getFrom(&mp);
this->packetHistory[this->packetHistoryTotalCount].id = mp.id;
this->packetHistory[this->packetHistoryTotalCount].reply_id = p.reply_id;
this->packetHistory[this->packetHistoryTotalCount].emoji = (bool)p.emoji;
this->packetHistory[this->packetHistoryTotalCount].payload_size = p.payload.size;
memcpy(this->packetHistory[this->packetHistoryTotalCount].payload, p.payload.bytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
if (this->storageType == StorageType::ST_PSRAM) {
this->packetHistory[this->packetHistoryTotalCount].time = getTime();
this->packetHistory[this->packetHistoryTotalCount].to = mp.to;
this->packetHistory[this->packetHistoryTotalCount].channel = mp.channel;
this->packetHistory[this->packetHistoryTotalCount].from = getFrom(&mp);
this->packetHistory[this->packetHistoryTotalCount].id = mp.id;
this->packetHistory[this->packetHistoryTotalCount].reply_id = p.reply_id;
this->packetHistory[this->packetHistoryTotalCount].emoji = (bool)p.emoji;
this->packetHistory[this->packetHistoryTotalCount].payload_size = p.payload.size;
memcpy(this->packetHistory[this->packetHistoryTotalCount].payload, p.payload.bytes,
meshtastic_Constants_DATA_PAYLOAD_LEN);
} else if (this->storageType == StorageType::ST_SDCARD) {
// Save to SDCARD
#if defined(HAS_SDCARD)
#if defined(ARCH_ESP32) || defined(ARCH_NRF52)
this->packetHistory[0].time = getTime();
this->packetHistory[0].to = mp.to;
this->packetHistory[0].channel = mp.channel;
this->packetHistory[0].from = getFrom(&mp);
this->packetHistory[0].id = mp.id;
this->packetHistory[0].reply_id = p.reply_id;
this->packetHistory[0].emoji = (bool)p.emoji;
this->packetHistory[0].payload_size = p.payload.size;
memcpy(this->packetHistory[0].payload, p.payload.bytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
spiLock->lock();
auto handler = SD.open("/storeforward/" + String(this->packetHistoryTotalCount), FILE_WRITE, true);
handler.write((uint8_t *)&this->packetHistory[0], sizeof(PacketHistoryStruct));
handler.close();
spiLock->unlock();
#endif
#endif
} else {
LOG_ERROR("S&F: Unknown storage type");
}
this->packetHistoryTotalCount++;
}
@@ -236,50 +314,108 @@ bool StoreForwardModule::sendPayload(NodeNum dest, uint32_t last_time)
meshtastic_MeshPacket *StoreForwardModule::preparePayload(NodeNum dest, uint32_t last_time, bool local)
{
for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) {
if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) {
/* Copy the messages that were received by the server in the last msAgo
to the packetHistoryTXQueue structure.
Client not interested in packets from itself and only in broadcast packets or packets towards it. */
if (this->packetHistory[i].from != dest &&
(this->packetHistory[i].to == NODENUM_BROADCAST || this->packetHistory[i].to == dest)) {
if (this->storageType == StorageType::ST_PSRAM) {
meshtastic_MeshPacket *p = allocDataPacket();
if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) {
/* Copy the messages that were received by the server in the last msAgo
to the packetHistoryTXQueue structure.
Client not interested in packets from itself and only in broadcast packets or packets towards it. */
if (this->packetHistory[i].from != dest &&
(this->packetHistory[i].to == NODENUM_BROADCAST || this->packetHistory[i].to == dest)) {
p->to = local ? this->packetHistory[i].to : dest; // PhoneAPI can handle original `to`
p->from = this->packetHistory[i].from;
p->id = this->packetHistory[i].id;
p->channel = this->packetHistory[i].channel;
p->decoded.reply_id = this->packetHistory[i].reply_id;
p->rx_time = this->packetHistory[i].time;
p->decoded.emoji = (uint32_t)this->packetHistory[i].emoji;
meshtastic_MeshPacket *p = allocDataPacket();
// Let's assume that if the server received the S&F request that the client is in range.
// TODO: Make this configurable.
p->want_ack = false;
p->to = local ? this->packetHistory[i].to : dest; // PhoneAPI can handle original `to`
p->from = this->packetHistory[i].from;
p->id = this->packetHistory[i].id;
p->channel = this->packetHistory[i].channel;
p->decoded.reply_id = this->packetHistory[i].reply_id;
p->rx_time = this->packetHistory[i].time;
p->decoded.emoji = (uint32_t)this->packetHistory[i].emoji;
// Let's assume that if the server received the S&F request that the client is in range.
// TODO: Make this configurable.
p->want_ack = false;
if (local) { // PhoneAPI gets normal TEXT_MESSAGE_APP
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
memcpy(p->decoded.payload.bytes, this->packetHistory[i].payload, this->packetHistory[i].payload_size);
p->decoded.payload.size = this->packetHistory[i].payload_size;
if (local) { // PhoneAPI gets normal TEXT_MESSAGE_APP
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
memcpy(p->decoded.payload.bytes, this->packetHistory[i].payload, this->packetHistory[i].payload_size);
p->decoded.payload.size = this->packetHistory[i].payload_size;
} else {
meshtastic_StoreAndForward sf = meshtastic_StoreAndForward_init_zero;
sf.which_variant = meshtastic_StoreAndForward_text_tag;
sf.variant.text.size = this->packetHistory[i].payload_size;
memcpy(sf.variant.text.bytes, this->packetHistory[i].payload, this->packetHistory[i].payload_size);
if (this->packetHistory[i].to == NODENUM_BROADCAST) {
sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_TEXT_BROADCAST;
} else {
sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_TEXT_DIRECT;
meshtastic_StoreAndForward sf = meshtastic_StoreAndForward_init_zero;
sf.which_variant = meshtastic_StoreAndForward_text_tag;
sf.variant.text.size = this->packetHistory[i].payload_size;
memcpy(sf.variant.text.bytes, this->packetHistory[i].payload, this->packetHistory[i].payload_size);
if (this->packetHistory[i].to == NODENUM_BROADCAST) {
sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_TEXT_BROADCAST;
} else {
sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_TEXT_DIRECT;
}
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes),
&meshtastic_StoreAndForward_msg, &sf);
}
p->decoded.payload.size = pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes),
&meshtastic_StoreAndForward_msg, &sf);
lastRequest[dest] = i + 1; // Update the last request index for the client device
return p;
}
lastRequest[dest] = i + 1; // Update the last request index for the client device
return p;
}
} else if (this->storageType == StorageType::ST_SDCARD) {
#if defined(HAS_SDCARD)
#if defined(ARCH_ESP32) || defined(ARCH_NRF52)
spiLock->lock();
auto handler = SD.open("/storeforward/" + String(i), FILE_READ);
if (handler) {
handler.read((uint8_t *)&this->packetHistory[0], sizeof(PacketHistoryStruct));
handler.close();
spiLock->unlock();
if (this->packetHistory[0].time && (this->packetHistory[0].time > last_time)) {
if (this->packetHistory[0].from != dest &&
(this->packetHistory[0].to == NODENUM_BROADCAST || this->packetHistory[0].to == dest)) {
meshtastic_MeshPacket *p = allocDataPacket();
p->to = local ? this->packetHistory[0].to : dest; // PhoneAPI can handle original `to`
p->from = this->packetHistory[0].from;
p->channel = this->packetHistory[0].channel;
p->rx_time = this->packetHistory[0].time;
// Let's assume that if the server received the S&F request that the client is in range.
p->want_ack = false;
if (local) { // PhoneAPI gets normal TEXT_MESSAGE_APP
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
memcpy(p->decoded.payload.bytes, this->packetHistory[0].payload, this->packetHistory[0].payload_size);
p->decoded.payload.size = this->packetHistory[0].payload_size;
} else {
meshtastic_StoreAndForward sf = meshtastic_StoreAndForward_init_zero;
sf.which_variant = meshtastic_StoreAndForward_text_tag;
sf.variant.text.size = this->packetHistory[0].payload_size;
memcpy(sf.variant.text.bytes, this->packetHistory[0].payload, this->packetHistory[0].payload_size);
if (this->packetHistory[0].to == NODENUM_BROADCAST) {
sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_TEXT_BROADCAST;
} else {
sf.rr = meshtastic_StoreAndForward_RequestResponse_ROUTER_TEXT_DIRECT;
}
p->decoded.payload.size = pb_encode_to_bytes(
p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_StoreAndForward_msg, &sf);
}
lastRequest[dest] = i + 1; // Update the last request index for the client device
return p;
}
}
} else {
spiLock->unlock();
}
#endif
#endif
} else {
LOG_ERROR("S&F: Unknown storage type");
}
}
return nullptr;
@@ -383,7 +519,7 @@ void StoreForwardModule::statsSend(uint32_t to)
*/
ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &mp)
{
#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(HAS_SDCARD)
if (moduleConfig.store_forward.enabled) {
if ((mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) && is_server) {
@@ -562,7 +698,7 @@ StoreForwardModule::StoreForwardModule()
ProtobufModule("StoreForward", meshtastic_PortNum_STORE_FORWARD_APP, &meshtastic_StoreAndForward_msg)
{
#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
#if defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(HAS_SDCARD)
isPromiscuous = true; // Brown chicken brown cow
@@ -613,7 +749,14 @@ StoreForwardModule::StoreForwardModule()
} else {
LOG_INFO("S&F: device doesn't have PSRAM, Disable");
}
#ifdef HAS_SDCARD
// If we have an SDCARD, format it for store&forward use
if (SD.cardType() != CARD_NONE) {
this->populateSDCard();
LOG_INFO("S&F: SDCARD initialized");
is_server = true;
}
#endif
// Client
} else {
is_client = true;

View File

@@ -9,6 +9,11 @@
#include <functional>
#include <unordered_map>
#ifdef HAS_SDCARD
#include "SPILock.h"
#include <SD.h>
#endif
struct PacketHistoryStruct {
uint32_t time;
uint32_t to;
@@ -21,6 +26,9 @@ struct PacketHistoryStruct {
pb_size_t payload_size;
};
// enum for the storage type
enum StorageType { ST_PSRAM, ST_SDCARD };
class StoreForwardModule : private concurrency::OSThread, public ProtobufModule<meshtastic_StoreAndForward>
{
bool busy = 0;
@@ -83,6 +91,10 @@ class StoreForwardModule : private concurrency::OSThread, public ProtobufModule<
private:
void populatePSRAM();
void populateSDCard();
// Storage Type
StorageType storageType = ST_PSRAM;
// S&F Defaults
uint32_t historyReturnMax = 25; // Return maximum of 25 records by default.

View File

@@ -559,8 +559,10 @@ void MQTT::sendSubscriptions()
int32_t MQTT::runOnce()
{
#if HAS_NETWORKING
if (!moduleConfig.mqtt.enabled || !(moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled()))
return disable();
bool wantConnection = wantsLink();
perhapsReportToMap();
@@ -570,7 +572,7 @@ int32_t MQTT::runOnce()
publishQueuedMessages();
return 200;
}
#if HAS_NETWORKING
else if (!pubSub.loop()) {
if (!wantConnection)
return 5000; // If we don't want connection now, check again in 5 secs
@@ -594,10 +596,8 @@ int32_t MQTT::runOnce()
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // Suppress entering light sleep (because that would turn off bluetooth)
return 20;
}
#else
// No networking available, return default interval
return 30000;
#endif
return 30000;
}
bool MQTT::isValidConfig(const meshtastic_ModuleConfig_MQTTConfig &config, MQTTClient *client)

View File

@@ -49,8 +49,6 @@
#define HW_VENDOR meshtastic_HardwareModel_RAK2560
#elif defined(WISMESH_TAP)
#define HW_VENDOR meshtastic_HardwareModel_WISMESH_TAP
#elif defined(WISMESH_TAG)
#define HW_VENDOR meshtastic_HardwareModel_WISMESH_TAG
#elif defined(GAT562_MESH_TRIAL_TRACKER)
#define HW_VENDOR meshtastic_HardwareModel_GAT562_MESH_TRIAL_TRACKER
#elif defined(RAK4630)
@@ -91,8 +89,6 @@
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_POCKET
#elif defined(NOMADSTAR_METEOR_PRO)
#define HW_VENDOR meshtastic_HardwareModel_NOMADSTAR_METEOR_PRO
#elif defined(SEEED_WIO_TRACKER_L1_EINK)
#define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1_EINK
#elif defined(SEEED_WIO_TRACKER_L1)
#define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1
#else

View File

@@ -61,7 +61,9 @@ class XModemAdapter
uint16_t packetno = 0;
#if defined(ARCH_NRF52) || defined(ARCH_STM32WL)
#if defined(ARCH_NRF52)
Adafruit_LittleFS_Namespace::File file = Adafruit_LittleFS_Namespace::File(FSCom);
#elif defined(ARCH_STM32WL)
File file = File(FSCom);
#else
File file;

View File

@@ -1,10 +1,10 @@
{
// "USERPREFS_BUTTON_PIN": "36",
"USERPREFS_CHANNELS_TO_WRITE": "1",
// "USERPREFS_CHANNELS_TO_WRITE": "3",
// "USERPREFS_CHANNEL_0_DOWNLINK_ENABLED": "false",
"USERPREFS_CHANNEL_0_NAME": "Everyone",
"USERPREFS_CHANNEL_0_PRECISION": "0",
"USERPREFS_CHANNEL_0_PSK": "{ 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 }",
// "USERPREFS_CHANNEL_0_NAME": "REPLACEME",
// "USERPREFS_CHANNEL_0_PRECISION": "14",
// "USERPREFS_CHANNEL_0_PSK": "{ 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 }",
// "USERPREFS_CHANNEL_0_UPLINK_ENABLED": "true",
// "USERPREFS_CHANNEL_1_DOWNLINK_ENABLED": "false",
// "USERPREFS_CHANNEL_1_NAME": "NodeChat",
@@ -17,12 +17,12 @@
// "USERPREFS_CHANNEL_2_PSK": "{ 0x15, 0x6f, 0xfe, 0x46, 0xd4, 0x56, 0x63, 0x8a, 0x54, 0x43, 0x13, 0xf2, 0xef, 0x6c, 0x63, 0x89, 0xf0, 0x06, 0x30, 0x52, 0xce, 0x36, 0x5e, 0xb1, 0xe8, 0xbb, 0x86, 0xe6, 0x26, 0x5b, 0x1d, 0x58 }",
// "USERPREFS_CHANNEL_2_UPLINK_ENABLED": "false",
// "USERPREFS_CONFIG_GPS_MODE": "meshtastic_Config_PositionConfig_GpsMode_ENABLED",
"USERPREFS_CONFIG_LORA_IGNORE_MQTT": "true",
// "USERPREFS_CONFIG_LORA_IGNORE_MQTT": "true",
// "USERPREFS_CONFIG_LORA_REGION": "meshtastic_Config_LoRaConfig_RegionCode_US",
// "USERPREFS_CONFIG_OWNER_LONG_NAME": "My Long Name",
// "USERPREFS_CONFIG_OWNER_SHORT_NAME": "MLN",
// "USERPREFS_CONFIG_DEVICE_ROLE": "meshtastic_Config_DeviceConfig_Role_CLIENT", // Defaults to CLIENT. ROUTER*, LOST AND FOUND, and REPEATER roles are restricted.
"USERPREFS_EVENT_MODE": "1",
// "USERPREFS_EVENT_MODE": "1",
// "USERPREFS_FIXED_BLUETOOTH": "121212",
// "USERPREFS_FIXED_GPS": "",
// "USERPREFS_FIXED_GPS_ALT": "0",
@@ -32,16 +32,16 @@
// "USERPREFS_CONFIG_GPS_UPDATE_INTERVAL": "600",
// "USERPREFS_CONFIG_POSITION_BROADCAST_INTERVAL": "1800",
// "USERPREFS_CONFIG_DEVICE_TELEM_UPDATE_INTERVAL": "900", // Device telemetry update interval in seconds
"USERPREFS_LORACONFIG_CHANNEL_NUM": "31",
"USERPREFS_LORACONFIG_MODEM_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO",
// "USERPREFS_LORACONFIG_CHANNEL_NUM": "31",
// "USERPREFS_LORACONFIG_MODEM_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST",
// "USERPREFS_USE_ADMIN_KEY_0": "{ 0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, 0x04, 0x1a, 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c }",
// "USERPREFS_USE_ADMIN_KEY_1": "{}",
// "USERPREFS_USE_ADMIN_KEY_2": "{}",
"USERPREFS_OEM_TEXT": "Burning Mesh 2025",
"USERPREFS_OEM_FONT_SIZE": "0",
"USERPREFS_OEM_IMAGE_WIDTH": "36",
"USERPREFS_OEM_IMAGE_HEIGHT": "56",
"USERPREFS_OEM_IMAGE_DATA": "{ 0x06, 0x00, 0x06, 0x00, 0x06, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x1F, 0x80, 0x1F, 0x80, 0x0F, 0x1E, 0xC0, 0x3F, 0x80, 0x07, 0x3C, 0xE0, 0x7F, 0xC0, 0x03, 0x7C, 0xF0, 0xF9, 0xE0, 0x03, 0xF8, 0xF8, 0xF0, 0xF1, 0x01, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0xE0, 0xF1, 0xF0, 0x78, 0x00, 0xE0, 0xE3, 0x79, 0x7C, 0x00, 0xC0, 0xE3, 0x79, 0x3C, 0x00, 0x80, 0xC7, 0x3F, 0x1E, 0x00, 0x80, 0xC7, 0x3F, 0x1E, 0x00, 0x00, 0x8F, 0x1F, 0x0F, 0x00, 0x00, 0x8F, 0x0F, 0x0F, 0x00, 0x00, 0x1E, 0x8F, 0x07, 0x00, 0x00, 0x1E, 0x86, 0x07, 0x00, 0x00, 0x3C, 0xC0, 0x03, 0x00, 0x00, 0x3C, 0xC0, 0x03, 0x00, 0x00, 0x78, 0xE0, 0x01, 0x00, 0x00, 0x78, 0xE0, 0x01, 0x00, 0x00, 0x70, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0xE0, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x70, 0x00, 0x00, 0x00, 0xE0, 0x70, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x70, 0xE0, 0x00, 0x00, 0x00, 0x78, 0xE0, 0x01, 0x00, 0x00, 0x78, 0xE0, 0x01, 0x00, 0x00, 0x78, 0xE0, 0x01, 0x00, 0x00, 0x38, 0xC0, 0x01, 0x00, 0x00, 0x3C, 0xC0, 0x03, 0x00, 0x00, 0x3C, 0xC0, 0x03, 0x00, 0x00, 0x1C, 0x80, 0x03, 0x00, 0x00, 0x1E, 0x80, 0x07, 0x00, 0x00, 0x1E, 0x80, 0x07, 0x00, 0x00, 0x0F, 0x00, 0x07, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x80, 0x07, 0x00, 0x1E, 0x00, 0x80, 0x07, 0x00, 0x1E, 0x00, 0xC0, 0x03, 0x00, 0x1C, 0x00, 0xC0, 0x03, 0x00, 0x3C, 0x00, 0xC0, 0x03, 0x00, 0x3C, 0x00, 0xC0, 0x01, 0x00, 0x38, 0x00, 0xC0, 0x00, 0x00, 0x30, 0x00 }",
// "USERPREFS_OEM_TEXT": "Caterham Car Club",
// "USERPREFS_OEM_FONT_SIZE": "0",
// "USERPREFS_OEM_IMAGE_WIDTH": "50",
// "USERPREFS_OEM_IMAGE_HEIGHT": "28",
// "USERPREFS_OEM_IMAGE_DATA": "{ 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0x61, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0x67, 0x00, 0x00, 0x00, 0x18, 0x1F, 0xF0, 0x67, 0x00, 0x00, 0x00, 0x30, 0x1F, 0xF8, 0x33, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFC, 0x31, 0x00, 0x00, 0x00, 0x60, 0x00, 0xFE, 0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x7E, 0x18, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x3F, 0x0C, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x1F, 0x0C, 0x00, 0x00, 0x00, 0x80, 0x81, 0x1F, 0x06, 0x00, 0x00, 0x00, 0x80, 0xC1, 0x0F, 0x06, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xC7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00}",
// "USERPREFS_NETWORK_ENABLED_PROTOCOLS": "1", // Enable UDP mesh
// "USERPREFS_NETWORK_WIFI_ENABLED": "true",
// "USERPREFS_NETWORK_WIFI_SSID": "wifi_ssid",
@@ -52,8 +52,8 @@
// "USERPREFS_MQTT_PASSWORD": "large4cats",
// "USERPREFS_MQTT_ENCRYPTION_ENABLED": "true",
// "USERPREFS_MQTT_TLS_ENABLED": "false",
"USERPREFS_MQTT_ROOT_TOPIC": "event/BurningMesh",
// "USERPREFS_MQTT_ROOT_TOPIC": "event/REPLACEME",
// "USERPREFS_RINGTONE_NAG_SECS": "60",
"USERPREFS_RINGTONE_RTTTL": "24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p",
"USERPREFS_TZ_STRING": "PST8PDT,M3.2.0,M11.1.0"
"USERPREFS_TZ_STRING": "tzplaceholder "
}

View File

@@ -9,9 +9,19 @@ build_flags =
-DSERIAL_UART_INSTANCE=1
-DPIN_SERIAL_RX=PA3
-DPIN_SERIAL_TX=PA2
-DHAL_DAC_MODULE_ONLY
-DHAL_RNG_MODULE_ENABLED
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
-DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1
-DMESHTASTIC_EXCLUDE_I2C=1
-DMESHTASTIC_EXCLUDE_WIFI=1
-DMESHTASTIC_EXCLUDE_BLUETOOTH=1
-DMESHTASTIC_EXCLUDE_GPS=1
-DMESHTASTIC_EXCLUDE_SCREEN=1
-DMESHTASTIC_EXCLUDE_MQTT=1
-DMESHTASTIC_EXCLUDE_POWERMON=1
;-DPIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF
;-DCFG_DEBUG

View File

@@ -6,7 +6,7 @@
// SD card - TODO: test, currently untested, copied from T3S3 variant
#define HAS_SDCARD
#define SDCARD_USE_SPI1
#define SDCARD_USE_HSPI
// TODO: rename this to make this SD-card specific
#define SPI_CS 13
#define SPI_SCK 14

View File

@@ -5,7 +5,7 @@
// #define HAS_SCREEN 0
// #define HAS_SDCARD
// #define SDCARD_USE_SPI1
// #define SDCARD_USE_HSPI
#define USE_SSD1306
#define I2C_SDA 12

View File

@@ -70,7 +70,7 @@
#endif
#define HAS_SDCARD // Have SPI interface SD card slot
#define SDCARD_USE_SPI1
#define SDCARD_USE_HSPI
#define LORA_RESET 3
#define LORA_SCK 12

View File

@@ -4,7 +4,7 @@
// #define HAS_SCREEN 0
// #define HAS_SDCARD
// #define SDCARD_USE_SPI1
// #define SDCARD_USE_HSPI
// #define USE_SSD1306

View File

@@ -222,7 +222,6 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
// #define PIN_GPS_EN PIN_3V3_EN
#define PIN_GPS_PPS (17) // Pulse per second input from the GPS
#define GPS_SERIAL_PORT Serial2
// On RAK2560 the GPS is be on a different UART
// #define GPS_RX_PIN PIN_SERIAL2_RX
// #define GPS_TX_PIN PIN_SERIAL2_TX

View File

@@ -7,8 +7,18 @@ build_flags =
-Ivariants/rak3172
-DPIN_WIRE_SDA=PA11
-DPIN_WIRE_SCL=PA12
-DHAL_DAC_MODULE_ONLY
-DHAL_RNG_MODULE_ENABLED
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
-DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1
-DMESHTASTIC_EXCLUDE_I2C=1
-DMESHTASTIC_EXCLUDE_WIFI=1
-DMESHTASTIC_EXCLUDE_BLUETOOTH=1
-DMESHTASTIC_EXCLUDE_GPS=1
-DMESHTASTIC_EXCLUDE_SCREEN=1
-DMESHTASTIC_EXCLUDE_MQTT=1
-DMESHTASTIC_EXCLUDE_POWERMON=1
;-DCFG_DEBUG
upload_port = stlink

View File

@@ -20,6 +20,8 @@ lib_deps =
rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2
beegee-tokyo/RAK12035_SoilMoisture@^1.0.4
https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip
https://github.com/Woutvstk/SdFat_wrapper25.git#6f8f48d56c15cbeac753560dfeede4a487f81f4c
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds

View File

@@ -113,11 +113,14 @@ static const uint8_t AREF = PIN_AREF;
* SPI Interfaces
*/
#define SPI_INTERFACES_COUNT 2
#define SPI_32MHZ_INTERFACE 0 // 0: use SPIM3 for SPI and SPIM2 for SPI1; 1: the opposite
// SPI pins for SX1262
#define PIN_SPI_MISO (45)
#define PIN_SPI_MOSI (44)
#define PIN_SPI_SCK (43)
// SPI1 pins for external(rak4630) spi (incl. SDCard)
#define PIN_SPI1_MISO (29) // (0 + 29)
#define PIN_SPI1_MOSI (30) // (0 + 30)
#define PIN_SPI1_SCK (3) // (0 + 3)
@@ -127,6 +130,19 @@ static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK = PIN_SPI_SCK;
// SD card SPI pin definitions
#define HAS_SDCARD 1
#define SDCARD_USE_SPI1 1
#define SDCARD_CS (26)
// Some settings for the SdFat library to optimize flash usage
#define SDFAT_FILE_TYPE 1 // only support FAT16/FAT32, not exFAT
#define CHECK_FLASH_PROGRAMMING \
0 // this reduces flash usage but may cause higher power usage when sd card is idle TODO:Check if power usage is higher
#define MAINTAIN_FREE_CLUSTER_COUNT 1 // maintain free cluster count
/*
* eink display pins
*/

View File

@@ -1,15 +0,0 @@
; The very slick RAK wireless RAK 4631 / 4630 board - Unified firmware for 5005/19003, with or without OLED RAK 1921
[env:rak_wismeshtag]
extends = nrf52840_base
board = wiscore_rak4631
board_check = true
build_flags = ${nrf52840_base.build_flags} -Ivariants/rak_wismeshtag -D WISMESH_TAG -D RAK_4631
-L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard"
-DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely.
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
-DMESHTASTIC_EXCLUDE_WIFI=1
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak_wismeshtag>
lib_deps =
${nrf52840_base.lib_deps}

View File

@@ -1,45 +0,0 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "variant.h"
#include "nrf.h"
#include "wiring_constants.h"
#include "wiring_digital.h"
const uint32_t g_ADigitalPinMap[] = {
// P0
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
// P1
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
void initVariant()
{
// LED1 & LED2
pinMode(PIN_LED1, OUTPUT);
ledOff(PIN_LED1);
pinMode(PIN_LED2, OUTPUT);
ledOff(PIN_LED2);
// 3V3 Power Rail
pinMode(PIN_3V3_EN, OUTPUT);
digitalWrite(PIN_3V3_EN, HIGH);
}

View File

@@ -1,245 +0,0 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _VARIANT_RAK4630_
#define _VARIANT_RAK4630_
#define RAK4630
/** Master clock frequency */
#define VARIANT_MCK (64000000ul)
#define USE_LFXO // Board uses 32khz crystal for LF
// define USE_LFRC // Board uses RC for LF
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "WVariant.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Number of pins defined in PinDescription array
#define PINS_COUNT (48)
#define NUM_DIGITAL_PINS (48)
#define NUM_ANALOG_INPUTS (6)
#define NUM_ANALOG_OUTPUTS (0)
// LEDs
#define PIN_LED1 (35)
#define PIN_LED2 (36)
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1
#define LED_BLUE PIN_LED2
#define LED_STATE_ON 1 // State when LED is litted
/*
* Buttons
*/
#define PIN_BUTTON1 9 // Pin for button on E-ink button module or IO expansion
#define BUTTON_NEED_PULLUP
#define PIN_BUTTON2 12
#define PIN_BUTTON3 24
#define PIN_BUTTON4 25
/*
* Analog pins
*/
#define PIN_A0 (5)
#define PIN_A1 (31)
#define PIN_A2 (28)
#define PIN_A3 (29)
#define PIN_A4 (30)
#define PIN_A5 (31)
#define PIN_A6 (0xff)
#define PIN_A7 (0xff)
static const uint8_t A0 = PIN_A0;
static const uint8_t A1 = PIN_A1;
static const uint8_t A2 = PIN_A2;
static const uint8_t A3 = PIN_A3;
static const uint8_t A4 = PIN_A4;
static const uint8_t A5 = PIN_A5;
static const uint8_t A6 = PIN_A6;
static const uint8_t A7 = PIN_A7;
#define ADC_RESOLUTION 14
// Other pins
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
/*
* Serial interfaces
*/
#define PIN_SERIAL1_RX (15)
#define PIN_SERIAL1_TX (16)
// Connected to Jlink CDC
#define PIN_SERIAL2_RX (8)
#define PIN_SERIAL2_TX (6)
/*
* SPI Interfaces
*/
#define SPI_INTERFACES_COUNT 2
#define PIN_SPI_MISO (45)
#define PIN_SPI_MOSI (44)
#define PIN_SPI_SCK (43)
#define PIN_SPI1_MISO (29) // (0 + 29)
#define PIN_SPI1_MOSI (30) // (0 + 30)
#define PIN_SPI1_SCK (3) // (0 + 3)
static const uint8_t SS = 42;
static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK = PIN_SPI_SCK;
/*
* eink display pins
*/
#define PIN_EINK_CS (0 + 26)
#define PIN_EINK_BUSY (0 + 4)
#define PIN_EINK_DC (0 + 17)
#define PIN_EINK_RES (-1)
#define PIN_EINK_SCLK (0 + 3)
#define PIN_EINK_MOSI (0 + 30) // also called SDI
/*
* Wire Interfaces
*/
#define WIRE_INTERFACES_COUNT 1
// RAK WISMESHTAG
#define PIN_WIRE_SDA (25)
#define PIN_WIRE_SCL (24)
// QSPI Pins
#define PIN_QSPI_SCK 3
#define PIN_QSPI_CS 26
#define PIN_QSPI_IO0 30
#define PIN_QSPI_IO1 29
#define PIN_QSPI_IO2 28
#define PIN_QSPI_IO3 2
/* @note RAK5005-O GPIO mapping to RAK4631 GPIO ports
RAK5005-O <-> nRF52840
IO1 <-> P0.17 (Arduino GPIO number 17)
IO2 <-> P1.02 (Arduino GPIO number 34)
IO3 <-> P0.21 (Arduino GPIO number 21)
IO4 <-> P0.04 (Arduino GPIO number 4)
IO5 <-> P0.09 (Arduino GPIO number 9)
IO6 <-> P0.10 (Arduino GPIO number 10)
IO7 <-> P0.28 (Arduino GPIO number 28)
SW1 <-> P0.01 (Arduino GPIO number 1)
A0 <-> P0.04/AIN2 (Arduino Analog A2
A1 <-> P0.31/AIN7 (Arduino Analog A7
SPI_CS <-> P0.26 (Arduino GPIO number 26)
*/
// RAK4630 LoRa module
/* Setup of the SX1262 LoRa module ( https://docs.rakwireless.com/Product-Categories/WisBlock/RAK4631/Datasheet/ )
P1.10 NSS SPI NSS (Arduino GPIO number 42)
P1.11 SCK SPI CLK (Arduino GPIO number 43)
P1.12 MOSI SPI MOSI (Arduino GPIO number 44)
P1.13 MISO SPI MISO (Arduino GPIO number 45)
P1.14 BUSY BUSY signal (Arduino GPIO number 46)
P1.15 DIO1 DIO1 event interrupt (Arduino GPIO number 47)
P1.06 NRESET NRESET manual reset of the SX1262 (Arduino GPIO number 38)
Important for successful SX1262 initialization:
* Setup DIO2 to control the antenna switch
* Setup DIO3 to control the TCXO power supply
* Setup the SX1262 to use it's DCDC regulator and not the LDO
* RAK4630 schematics show GPIO P1.07 connected to the antenna switch, but it should not be initialized, as DIO2 will do the
control of the antenna switch
SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
*/
#define DETECTION_SENSOR_EN 4
#define USE_SX1262
#define SX126X_CS (42)
#define SX126X_DIO1 (47)
#define SX126X_BUSY (46)
#define SX126X_RESET (38)
// #define SX126X_TXEN (39)
// #define SX126X_RXEN (37)
#define SX126X_POWER_EN (37)
// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// Testing USB detection
#define NRF_APM
// enables 3.3V periphery like GPS or IO Module
// Do not toggle this for GPS power savings
#define PIN_3V3_EN (34)
// RAK WISMESHTAG
#define PIN_GPS_EN PIN_3V3_EN
#define PIN_GPS_PPS (17) // Pulse per second input from the GPS
#define GPS_RX_PIN PIN_SERIAL1_RX
#define GPS_TX_PIN PIN_SERIAL1_TX
// RAK WISMESHTAG
#define PIN_BUZZER 21
// Battery
// The battery sense is hooked to pin A0 (5)
#define BATTERY_PIN PIN_A0
// and has 12 bit resolution
#define BATTERY_SENSE_RESOLUTION_BITS 12
#define BATTERY_SENSE_RESOLUTION 4096.0
#undef AREF_VOLTAGE
#define AREF_VOLTAGE 3.0
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER 1.73
#define RAK_4631 1
#ifdef __cplusplus
}
#endif
/*----------------------------------------------------------------------------
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif

View File

@@ -4,8 +4,7 @@ extends = nrf52840_base, inkhud
;board_level = extra
build_flags = ${nrf52840_base.build_flags} ${inkhud.build_flags}
-I $PROJECT_DIR/variants/seeed_wio_tracker_L1_eink
-D SEEED_WIO_TRACKER_L1_EINK
-D SEEED_WIO_TRACKER_L1
-D SEEED_WIO_TRACKER_L1
-Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52
board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/seeed_wio_tracker_L1_eink> ${inkhud.build_src_filter}

View File

@@ -56,7 +56,7 @@
#define GPS_1PPS_PIN 6
#define HAS_SDCARD // Have SPI interface SD card slot
#define SDCARD_USE_SPI1
#define SDCARD_USE_HSPI
// PCF8563 RTC Module
// #define PCF8563_RTC 0x51 //Putting definitions in variant. h does not compile correctly

View File

@@ -1,5 +1,5 @@
#define HAS_SDCARD
#define SDCARD_USE_SPI1
#define SDCARD_USE_HSPI
// Display (E-Ink)
#define PIN_EINK_CS 15

View File

@@ -1,5 +1,5 @@
#define HAS_SDCARD
#define SDCARD_USE_SPI1
#define SDCARD_USE_HSPI
#define USE_SSD1306
@@ -76,4 +76,4 @@
#endif
#define HAS_SDCARD // Have SPI interface SD card slot
#define SDCARD_USE_SPI1
#define SDCARD_USE_HSPI

View File

@@ -3,6 +3,11 @@ extends = esp32_base
board = ttgo-lora32-v21
board_check = true
build_flags =
${esp32_base.build_flags} -D TLORA_V2_1_16 -I variants/tlora_v2_1_16
${esp32_base.build_flags}
-D TLORA_V2_1_16
-I variants/tlora_v2_1_16
-DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely.
upload_speed = 115200
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX126X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
upload_speed = 115200

View File

@@ -22,4 +22,15 @@
#define LORA_DIO1 33 // https://www.thethingsnetwork.org/forum/t/big-esp32-sx127x-topic-part-3/18436
#endif
#define LORA_DIO2 32 // Not really used
#define LORA_DIO2 32 // Not really used
/*
* Use SD Card for Store and Forward
*/
#define HAS_SDCARD
#define SDCARD_USE_HSPI
#define SPI_MOSI 15
#define SPI_MISO 2
#define SPI_SCK 14
#define SPI_CS 13
#define SDCARD_CS SPI_CS

View File

@@ -51,6 +51,9 @@
#undef GPS_RX_PIN
#undef GPS_TX_PIN
// #define HAS_SDCARD 1 // causes hang if defined
#define SDCARD_USE_HSPI
#define SD_SPI_FREQUENCY 25000000
#define SDCARD_CS 43

View File

@@ -10,12 +10,12 @@ build_flags =
-DPIN_SERIAL_TX=PB6
-DPIN_WIRE_SDA=PA15
-DPIN_WIRE_SCL=PB15
-DHAS_SENSOR=1
-DENABLE_HWSERIAL2
-DPIN_SERIAL2_TX=PA2
-DPIN_SERIAL2_RX=PA3
-DHAS_GPS=1
-DGPS_SERIAL_PORT=Serial2
-DHAL_DAC_MODULE_ONLY
-DHAL_RNG_MODULE_ENABLED
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
-DHAS_SENSOR
upload_port = stlink

View File

@@ -17,8 +17,6 @@ Do not expect a working Meshtastic device with this target.
#define LED_PIN PB5
#define LED_STATE_ON 1
#define WIO_E5
#if (defined(LED_BUILTIN) && LED_BUILTIN == PNUM_NOT_DEFINED)
#undef LED_BUILTIN
#define LED_BUILTIN (LED_PIN)