This commit is contained in:
Thomas Göttgens 2025-06-07 23:09:19 +02:00 committed by GitHub
commit 658c196632
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 304 additions and 92 deletions

View File

@ -23,11 +23,16 @@ SPIClass SPI_HSPI(HSPI);
#else #else
#define SDHandler SPI #define SDHandler SPI
#endif #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 #ifndef SD_SPI_FREQUENCY
#define SD_SPI_FREQUENCY 4000000U #define SD_SPI_FREQUENCY 4000000U
#endif #endif
#endif // HAS_SDCARD #endif // HAS_SDCARD
/** /**
@ -309,7 +314,13 @@ void setupSDCard()
{ {
#if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI) #if defined(HAS_SDCARD) && !defined(SDCARD_USE_SOFT_SPI)
concurrency::LockGuard g(spiLock); concurrency::LockGuard g(spiLock);
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52))
#if (defined(ARCH_ESP32))
SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI); SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
#elif (defined(ARCH_NRF52))
SDHandler.begin();
#endif
if (!SD.begin(SDCARD_CS, SDHandler, SD_SPI_FREQUENCY)) { if (!SD.begin(SDCARD_CS, SDHandler, SD_SPI_FREQUENCY)) {
LOG_DEBUG("No SD_MMC card detected"); LOG_DEBUG("No SD_MMC card detected");
return; return;
@ -319,20 +330,23 @@ void setupSDCard()
LOG_DEBUG("No SD_MMC card attached"); LOG_DEBUG("No SD_MMC card attached");
return; return;
} }
LOG_DEBUG("SD_MMC Card Type: ");
if (cardType == CARD_MMC) { if (cardType == CARD_MMC) {
LOG_DEBUG("MMC"); LOG_DEBUG("SD_MMC Card Type: MMC");
} else if (cardType == CARD_SD) { } else if (cardType == CARD_SD) {
LOG_DEBUG("SDSC"); LOG_DEBUG("SD_MMC Card Type: SDSC");
} else if (cardType == CARD_SDHC) { } else if (cardType == CARD_SDHC) {
LOG_DEBUG("SDHC"); LOG_DEBUG("SD_MMC Card Type: SDHC");
} else { } else {
LOG_DEBUG("UNKNOWN"); LOG_DEBUG("SD_MMC Card Type: UNKNOWN");
} }
uint64_t cardSize = SD.cardSize() / (1024 * 1024); uint64_t cardSize = SD.cardSize() / (1024 * 1024);
LOG_DEBUG("SD Card Size: %lu MB", (uint32_t)cardSize); LOG_DEBUG("SD Card Size: %lu MB", (uint32_t)cardSize);
LOG_DEBUG("Total space: %lu MB", (uint32_t)(SD.totalBytes() / (1024 * 1024))); 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 #endif
} }

View File

@ -49,6 +49,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "modules/ExternalNotificationModule.h" #include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h" #include "modules/TextMessageModule.h"
#include "modules/WaypointModule.h" #include "modules/WaypointModule.h"
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
#include "modules/StoreForwardModule.h"
#endif
#include "sleep.h" #include "sleep.h"
#include "target_specific.h" #include "target_specific.h"
@ -58,11 +61,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
#include "esp_task_wdt.h" #include "esp_task_wdt.h"
#include "modules/StoreForwardModule.h"
#endif #endif
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
#include "modules/StoreForwardModule.h"
#include "platform/portduino/PortduinoGlue.h" #include "platform/portduino/PortduinoGlue.h"
#endif #endif
@ -2495,9 +2496,9 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
display->setColor(WHITE); display->setColor(WHITE);
// Draw the channel name // Draw the channel name
display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr); 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 // Draw our hardware ID to assist with bluetooth pairing. Either prefix with Info or S&F Logo
if (moduleConfig.store_forward.enabled) { if (moduleConfig.store_forward.enabled) {
#ifdef ARCH_ESP32
if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat, if (!Throttle::isWithinTimespanMs(storeForwardModule->lastHeartbeat,
(storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit (storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
@ -2524,6 +2525,9 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
imgSF); imgSF);
#endif #endif
} }
#else
// No store and forward, show a exclamation mark
if (false) {
#endif #endif
} else { } else {
// TODO: Raspberry Pi supports more than just the one screen size // TODO: Raspberry Pi supports more than just the one screen size

View File

@ -724,6 +724,8 @@ void setup()
i2cScanner.reset(); i2cScanner.reset();
#endif #endif
// initSPI() must have called at this point (must be before screen and lora)
#ifdef HAS_SDCARD #ifdef HAS_SDCARD
setupSDCard(); setupSDCard();
#endif #endif
@ -817,7 +819,6 @@ void setup()
drv.setMode(DRV2605_MODE_INTTRIG); drv.setMode(DRV2605_MODE_INTTRIG);
#endif #endif
// Init our SPI controller (must be before screen and lora)
#ifdef ARCH_RP2040 #ifdef ARCH_RP2040
#ifdef HW_SPI1_DEVICE #ifdef HW_SPI1_DEVICE
SPI1.setSCK(LORA_SCK); SPI1.setSCK(LORA_SCK);

View File

@ -57,6 +57,8 @@ uint32_t MemGet::getFreePsram()
{ {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
return ESP.getFreePsram(); return ESP.getFreePsram();
#elif (defined(HAS_SDCARD) && defined(ARCH_ESP32))
return SD.totalBytes() - SD.usedBytes();
#elif defined(ARCH_PORTDUINO) #elif defined(ARCH_PORTDUINO)
return 4194252; return 4194252;
#else #else
@ -73,6 +75,8 @@ uint32_t MemGet::getPsramSize()
{ {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
return ESP.getPsramSize(); return ESP.getPsramSize();
#elif (defined(HAS_SDCARD) && defined(ARCH_ESP32))
return SD.totalBytes();
#elif defined(ARCH_PORTDUINO) #elif defined(ARCH_PORTDUINO)
return 4194252; return 4194252;
#else #else

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -32,7 +32,7 @@ StoreForwardModule *storeForwardModule;
int32_t StoreForwardModule::runOnce() 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) { if (moduleConfig.store_forward.enabled && is_server) {
// Send out the message queue. // Send out the message queue.
if (this->busy) { 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(), LOG_DEBUG("After PSRAM init: heap %d/%d PSRAM %d/%d", memGet.getFreeHeap(), memGet.getHeapSize(), memGet.getFreePsram(),
memGet.getPsramSize()); memGet.getPsramSize());
LOG_DEBUG("numberOfPackets for packetHistory - %u", numberOfPackets); 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); lastRequest.emplace(dest, 0);
} }
for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) { for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) {
if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) { if (this->storageType == StorageType::ST_PSRAM) {
// Client is only interested in packets not from itself and only in broadcast packets or packets towards it. if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) {
if (this->packetHistory[i].from != dest && // Client is only interested in packets not from itself and only in broadcast packets or packets towards it.
(this->packetHistory[i].to == NODENUM_BROADCAST || this->packetHistory[i].to == dest)) { if (this->packetHistory[i].from != dest &&
count++; (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; return count;
} }
@ -187,23 +241,47 @@ void StoreForwardModule::historyAdd(const meshtastic_MeshPacket &mp)
const auto &p = mp.decoded; const auto &p = mp.decoded;
if (this->packetHistoryTotalCount == this->records) { if (this->packetHistoryTotalCount == this->records) {
LOG_WARN("S&F - PSRAM Full. Starting overwrite"); LOG_WARN("S&F - Storage Full. Starting overwrite");
this->packetHistoryTotalCount = 0; this->packetHistoryTotalCount = 0;
for (auto &i : lastRequest) { for (auto &i : lastRequest) {
i.second = 0; // Clear the last request index for each client device i.second = 0; // Clear the last request index for each client device
} }
} }
this->packetHistory[this->packetHistoryTotalCount].time = getTime(); if (this->storageType == StorageType::ST_PSRAM) {
this->packetHistory[this->packetHistoryTotalCount].to = mp.to; this->packetHistory[this->packetHistoryTotalCount].time = getTime();
this->packetHistory[this->packetHistoryTotalCount].channel = mp.channel; this->packetHistory[this->packetHistoryTotalCount].to = mp.to;
this->packetHistory[this->packetHistoryTotalCount].from = getFrom(&mp); this->packetHistory[this->packetHistoryTotalCount].channel = mp.channel;
this->packetHistory[this->packetHistoryTotalCount].id = mp.id; this->packetHistory[this->packetHistoryTotalCount].from = getFrom(&mp);
this->packetHistory[this->packetHistoryTotalCount].reply_id = p.reply_id; this->packetHistory[this->packetHistoryTotalCount].id = mp.id;
this->packetHistory[this->packetHistoryTotalCount].emoji = (bool)p.emoji; this->packetHistory[this->packetHistoryTotalCount].reply_id = p.reply_id;
this->packetHistory[this->packetHistoryTotalCount].payload_size = p.payload.size; this->packetHistory[this->packetHistoryTotalCount].emoji = (bool)p.emoji;
memcpy(this->packetHistory[this->packetHistoryTotalCount].payload, p.payload.bytes, meshtastic_Constants_DATA_PAYLOAD_LEN); 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++; 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) meshtastic_MeshPacket *StoreForwardModule::preparePayload(NodeNum dest, uint32_t last_time, bool local)
{ {
for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) { for (uint32_t i = lastRequest[dest]; i < this->packetHistoryTotalCount; i++) {
if (this->packetHistory[i].time && (this->packetHistory[i].time > last_time)) { if (this->storageType == StorageType::ST_PSRAM) {
/* 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)) {
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` meshtastic_MeshPacket *p = allocDataPacket();
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. p->to = local ? this->packetHistory[i].to : dest; // PhoneAPI can handle original `to`
// TODO: Make this configurable. p->from = this->packetHistory[i].from;
p->want_ack = false; 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 { } 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), lastRequest[dest] = i + 1; // Update the last request index for the client device
&meshtastic_StoreAndForward_msg, &sf);
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; return nullptr;
@ -383,7 +519,7 @@ void StoreForwardModule::statsSend(uint32_t to)
*/ */
ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &mp) 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 (moduleConfig.store_forward.enabled) {
if ((mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) && is_server) { 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) 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 isPromiscuous = true; // Brown chicken brown cow
@ -613,7 +749,14 @@ StoreForwardModule::StoreForwardModule()
} else { } else {
LOG_INFO("S&F: device doesn't have PSRAM, Disable"); 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 // Client
} else { } else {
is_client = true; is_client = true;

View File

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

View File

@ -61,7 +61,9 @@ class XModemAdapter
uint16_t packetno = 0; 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); File file = File(FSCom);
#else #else
File file; File file;

View File

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

View File

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

View File

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

View File

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

View File

@ -19,6 +19,8 @@ lib_deps =
https://github.com/RAKWireless/RAK13800-W5100S/archive/1.0.2.zip https://github.com/RAKWireless/RAK13800-W5100S/archive/1.0.2.zip
rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2 rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2
https://github.com/RAKWireless/RAK12034-BMX160/archive/dcead07ffa267d3c906e9ca4a1330ab989e957e2.zip 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) ; 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 ; Note: as of 6/2013 the serial/bootloader based programming takes approximately 30 seconds

View File

@ -108,11 +108,14 @@ static const uint8_t AREF = PIN_AREF;
* SPI Interfaces * SPI Interfaces
*/ */
#define SPI_INTERFACES_COUNT 2 #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_MISO (45)
#define PIN_SPI_MOSI (44) #define PIN_SPI_MOSI (44)
#define PIN_SPI_SCK (43) #define PIN_SPI_SCK (43)
// SPI1 pins for external(rak4630) spi (incl. SDCard)
#define PIN_SPI1_MISO (29) // (0 + 29) #define PIN_SPI1_MISO (29) // (0 + 29)
#define PIN_SPI1_MOSI (30) // (0 + 30) #define PIN_SPI1_MOSI (30) // (0 + 30)
#define PIN_SPI1_SCK (3) // (0 + 3) #define PIN_SPI1_SCK (3) // (0 + 3)
@ -122,6 +125,19 @@ static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO; static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK = PIN_SPI_SCK; 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 * eink display pins
*/ */

View File

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

View File

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

View File

@ -1,5 +1,5 @@
#define HAS_SDCARD #define HAS_SDCARD
#define SDCARD_USE_SPI1 #define SDCARD_USE_HSPI
#define USE_SSD1306 #define USE_SSD1306
@ -76,4 +76,4 @@
#endif #endif
#define HAS_SDCARD // Have SPI interface SD card slot #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 = ttgo-lora32-v21
board_check = true board_check = true
build_flags = 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. -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 #define LORA_DIO1 33 // https://www.thethingsnetwork.org/forum/t/big-esp32-sx127x-topic-part-3/18436
#endif #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_RX_PIN
#undef GPS_TX_PIN #undef GPS_TX_PIN
// #define HAS_SDCARD 1 // causes hang if defined
#define SDCARD_USE_HSPI
#define SD_SPI_FREQUENCY 25000000 #define SD_SPI_FREQUENCY 25000000
#define SDCARD_CS 43 #define SDCARD_CS 43