Compare commits

..

13 Commits

Author SHA1 Message Date
Jonathan Bennett
7373fce2e1 more work 2025-12-14 22:26:52 -06:00
Jonathan Bennett
f10d2fb8c2 more 2025-12-14 21:54:51 -06:00
Jonathan Bennett
4552efce81 More progress 2025-12-14 19:45:22 -06:00
Jonathan Bennett
ecdcbe5c09 fix 2025-12-14 17:44:30 -06:00
Jonathan Bennett
4dd43c7753 more 2025-12-14 17:31:40 -06:00
Jonathan Bennett
471da2e632 misc fixes 2025-12-14 17:07:52 -06:00
Jonathan Bennett
03961161af v2 2025-12-14 16:29:27 -06:00
Jonathan Bennett
dfe74653f2 More sfpp work 2025-12-14 15:58:41 -06:00
Jonathan Bennett
6b2bd09dd8 WIP sfpp 2025-12-13 20:27:04 -06:00
Jonathan Bennett
4fd7960371 Drop debug warning 2025-12-13 12:22:22 -06:00
Jonathan Bennett
5f0e86a27e Merge branch 'develop' into native-time 2025-12-13 12:10:18 -06:00
Jonathan Bennett
9ce1a7ff79 Detect if NTP is active on native 2025-12-13 12:08:24 -06:00
Austin
bf32f17f28 Actions: Compact manifest job output summary (#8957) 2025-12-13 12:32:01 +11:00
43 changed files with 1167 additions and 255 deletions

View File

@@ -76,7 +76,7 @@ runs:
done
- name: PlatformIO ${{ inputs.arch }} download cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: ~/.platformio/.cache
key: pio-cache-${{ inputs.arch }}-${{ hashFiles('.github/actions/**', '**.ini') }}
@@ -100,7 +100,7 @@ runs:
id: version
- name: Store binaries as an artifact
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}
overwrite: true

View File

@@ -64,7 +64,7 @@ jobs:
PKG_VERSION: ${{ steps.version.outputs.deb }}
- name: Store binaries as an artifact
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
overwrite: true

View File

@@ -56,19 +56,21 @@ jobs:
ota_firmware_source: ${{ steps.ota_dir.outputs.src || '' }}
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
- name: Echo manifest from release/firmware-*.mt.json to job summary
if: ${{ always() }}
- name: Job summary
env:
PIO_ENV: ${{ inputs.pio_env }}
run: |
echo "## Manifest: \`$PIO_ENV\`" >> $GITHUB_STEP_SUMMARY
echo "## $PIO_ENV" >> $GITHUB_STEP_SUMMARY
echo "<details><summary><strong>Manifest</strong></summary>" >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '```json' >> $GITHUB_STEP_SUMMARY
cat release/firmware-*.mt.json >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "</details>" >> $GITHUB_STEP_SUMMARY
- name: Store binaries as an artifact
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
id: upload
with:
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}

View File

@@ -98,7 +98,7 @@ jobs:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
path: ./
pattern: firmware-*-*
@@ -111,7 +111,7 @@ jobs:
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
overwrite: true
@@ -127,7 +127,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
pattern: firmware-*-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -146,7 +146,7 @@ jobs:
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
overwrite: true

View File

@@ -173,7 +173,7 @@ jobs:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
path: ./
pattern: firmware-${{matrix.arch}}-*
@@ -183,7 +183,7 @@ jobs:
run: ls -R
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true
@@ -200,7 +200,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -219,7 +219,7 @@ jobs:
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true
@@ -260,14 +260,14 @@ jobs:
Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
merge-multiple: true
path: ./output/debian-src
- name: Download `native-tft` pio deps
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -291,7 +291,7 @@ jobs:
}' > firmware-${{ needs.version.outputs.long }}.json
- name: Save Release manifest artifact
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: manifest-${{ needs.version.outputs.long }}
overwrite: true
@@ -332,7 +332,7 @@ jobs:
with:
python-version: 3.x
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -349,7 +349,7 @@ jobs:
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -388,14 +388,14 @@ jobs:
python-version: 3.x
- name: Get firmware artifacts
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./publish
- name: Get manifest artifact
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
pattern: manifest-${{ needs.version.outputs.long }}
path: ./publish

View File

@@ -147,7 +147,7 @@ jobs:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
path: ./
pattern: firmware-${{matrix.arch}}-*
@@ -160,7 +160,7 @@ jobs:
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true
@@ -176,7 +176,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -195,7 +195,7 @@ jobs:
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true
@@ -235,14 +235,14 @@ jobs:
Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
merge-multiple: true
path: ./output/debian-src
- name: Download `native-tft` pio deps
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -292,7 +292,7 @@ jobs:
with:
python-version: 3.x
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -309,7 +309,7 @@ jobs:
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -347,7 +347,7 @@ jobs:
with:
python-version: 3.x
- uses: actions/download-artifact@v7
- uses: actions/download-artifact@v6
with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true

View File

@@ -58,7 +58,7 @@ jobs:
id: version
- name: Download artifacts
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
merge-multiple: true

View File

@@ -56,7 +56,7 @@ jobs:
PLATFORMIO_CORE_DIR: pio/core
- name: Store binaries as an artifact
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }}
overwrite: true

View File

@@ -60,7 +60,7 @@ jobs:
id: version
- name: Download artifacts
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
merge-multiple: true

View File

@@ -50,7 +50,7 @@ jobs:
- name: Download test artifacts
if: needs.native-tests.result != 'skipped'
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
name: platformio-test-report-${{ steps.version.outputs.long }}
merge-multiple: true

View File

@@ -33,7 +33,7 @@ jobs:
# step 3
- name: save report as pipeline artifact
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: report.sarif
overwrite: true

View File

@@ -59,7 +59,7 @@ jobs:
id: version
- name: Save coverage information
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
if: always() # run this step even if previous step failed
with:
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}
@@ -94,7 +94,7 @@ jobs:
- name: Save test results
if: always() # run this step even if previous step failed
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: platformio-test-report-${{ steps.version.outputs.long }}
overwrite: true
@@ -108,7 +108,7 @@ jobs:
sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative.
- name: Save coverage information
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
if: always() # run this step even if previous step failed
with:
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}
@@ -137,7 +137,7 @@ jobs:
id: version
- name: Download test artifacts
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
name: platformio-test-report-${{ steps.version.outputs.long }}
merge-multiple: true
@@ -150,7 +150,7 @@ jobs:
reporter: java-junit
- name: Download coverage artifacts
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}
path: code-coverage-report
@@ -163,7 +163,7 @@ jobs:
genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report
- name: Save Code Coverage Report
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: code-coverage-report-${{ steps.version.outputs.long }}
path: code-coverage-report

View File

@@ -9,16 +9,16 @@ plugins:
lint:
enabled:
- checkov@3.2.495
- renovate@42.52.8
- renovate@42.42.2
- prettier@3.7.4
- trufflehog@3.92.3
- trufflehog@3.92.1
- yamllint@1.37.1
- bandit@1.9.2
- trivy@0.68.1
- taplo@0.10.0
- ruff@0.14.9
- ruff@0.14.8
- isort@7.0.0
- markdownlint@0.47.0
- markdownlint@0.46.0
- oxipng@10.0.0
- svgo@4.0.0
- actionlint@1.7.9

View File

@@ -184,8 +184,6 @@ Input:
Logging:
LogLevel: info # debug, info, warn, error
# TraceFile: /var/log/meshtasticd.json
# JSONFile: /packets.json # File location for JSON output of decoded packets
# JSONFilter: position # filter for packets to save to JSON file
# AsciiLogs: true # default if not specified is !isatty() on stdout
Webserver:

View File

@@ -123,7 +123,7 @@ lib_deps =
[device-ui_base]
lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/2746a1ce3804998460a2cb319b8ea8a238dfd8c9.zip
https://github.com/meshtastic/device-ui/archive/4fb5f24787caa841b58dbf623a52c4c5861d6722.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]

View File

@@ -53,7 +53,6 @@ typedef struct _InputEvent {
class InputPollable
{
public:
virtual ~InputPollable() = default;
virtual void pollOnce() = 0;
};

View File

@@ -3,9 +3,6 @@
#include "RotaryEncoderImpl.h"
#include "InputBroker.h"
#include "RotaryEncoder.h"
#ifdef ARCH_ESP32
#include "sleep.h"
#endif
#define ORIGIN_NAME "RotaryEncoder"
@@ -14,20 +11,6 @@ RotaryEncoderImpl *rotaryEncoderImpl;
RotaryEncoderImpl::RotaryEncoderImpl()
{
rotary = nullptr;
#ifdef ARCH_ESP32
isFirstInit = true;
#endif
}
RotaryEncoderImpl::~RotaryEncoderImpl()
{
LOG_DEBUG("RotaryEncoderImpl destructor");
detachRotaryEncoderInterrupts();
if (rotary != nullptr) {
delete rotary;
rotary = nullptr;
}
}
bool RotaryEncoderImpl::init()
@@ -42,22 +25,15 @@ bool RotaryEncoderImpl::init()
eventCcw = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_ccw);
eventPressed = static_cast<input_broker_event>(moduleConfig.canned_message.inputbroker_event_press);
if (rotary == nullptr) {
rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b,
moduleConfig.canned_message.inputbroker_pin_press);
}
rotary = new RotaryEncoder(moduleConfig.canned_message.inputbroker_pin_a, moduleConfig.canned_message.inputbroker_pin_b,
moduleConfig.canned_message.inputbroker_pin_press);
rotary->resetButton();
attachRotaryEncoderInterrupts();
#ifdef ARCH_ESP32
// Register callbacks for before and after lightsleep
// Used to detach and reattach interrupts
if (isFirstInit) {
lsObserver.observe(&notifyLightSleep);
lsEndObserver.observe(&notifyLightSleepEnd);
isFirstInit = false;
}
#endif
interruptInstance = this;
auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); };
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE);
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE);
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE);
LOG_INFO("RotaryEncoder initialized pins(%d, %d, %d), events(%d, %d, %d)", moduleConfig.canned_message.inputbroker_pin_a,
moduleConfig.canned_message.inputbroker_pin_b, moduleConfig.canned_message.inputbroker_pin_press, eventCw, eventCcw,
@@ -95,50 +71,6 @@ void RotaryEncoderImpl::pollOnce()
}
}
void RotaryEncoderImpl::detachRotaryEncoderInterrupts()
{
LOG_DEBUG("RotaryEncoderImpl detach button interrupts");
if (interruptInstance == this) {
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_a);
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_b);
detachInterrupt(moduleConfig.canned_message.inputbroker_pin_press);
interruptInstance = nullptr;
} else {
LOG_WARN("RotaryEncoderImpl: interrupts already detached");
}
}
void RotaryEncoderImpl::attachRotaryEncoderInterrupts()
{
LOG_DEBUG("RotaryEncoderImpl attach button interrupts");
if (rotary != nullptr && interruptInstance == nullptr) {
rotary->resetButton();
interruptInstance = this;
auto interruptHandler = []() { inputBroker->requestPollSoon(interruptInstance); };
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_a, interruptHandler, CHANGE);
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_b, interruptHandler, CHANGE);
attachInterrupt(moduleConfig.canned_message.inputbroker_pin_press, interruptHandler, CHANGE);
} else {
LOG_WARN("RotaryEncoderImpl: interrupts already attached");
}
}
#ifdef ARCH_ESP32
int RotaryEncoderImpl::beforeLightSleep(void *unused)
{
detachRotaryEncoderInterrupts();
return 0; // Indicates success;
}
int RotaryEncoderImpl::afterLightSleep(esp_sleep_wakeup_cause_t cause)
{
attachRotaryEncoderInterrupts();
return 0; // Indicates success;
}
#endif
RotaryEncoderImpl *RotaryEncoderImpl::interruptInstance;
#endif

View File

@@ -8,18 +8,12 @@
class RotaryEncoder;
class RotaryEncoderImpl final : public InputPollable
class RotaryEncoderImpl : public InputPollable
{
public:
RotaryEncoderImpl();
~RotaryEncoderImpl() override;
bool init();
bool init(void);
virtual void pollOnce() override;
// Disconnect and reconnect interrupts for light sleep
#ifdef ARCH_ESP32
int beforeLightSleep(void *unused);
int afterLightSleep(esp_sleep_wakeup_cause_t cause);
#endif
protected:
static RotaryEncoderImpl *interruptInstance;
@@ -29,21 +23,6 @@ class RotaryEncoderImpl final : public InputPollable
input_broker_event eventPressed = INPUT_BROKER_NONE;
RotaryEncoder *rotary;
private:
#ifdef ARCH_ESP32
bool isFirstInit;
#endif
void detachRotaryEncoderInterrupts();
void attachRotaryEncoderInterrupts();
#ifdef ARCH_ESP32
// Get notified when lightsleep begins and ends
CallbackObserver<RotaryEncoderImpl, void *> lsObserver =
CallbackObserver<RotaryEncoderImpl, void *>(this, &RotaryEncoderImpl::beforeLightSleep);
CallbackObserver<RotaryEncoderImpl, esp_sleep_wakeup_cause_t> lsEndObserver =
CallbackObserver<RotaryEncoderImpl, esp_sleep_wakeup_cause_t>(this, &RotaryEncoderImpl::afterLightSleep);
#endif
};
extern RotaryEncoderImpl *rotaryEncoderImpl;

View File

@@ -428,10 +428,17 @@ void setup()
#endif
#if ARCH_PORTDUINO
RTCQuality ourQuality = RTCQualityDevice;
std::string timeCommandResult = exec("timedatectl status | grep synchronized | grep yes -c");
if (timeCommandResult[0] == '1') {
ourQuality = RTCQualityNTP;
}
struct timeval tv;
tv.tv_sec = time(NULL);
tv.tv_usec = 0;
perhapsSetRTC(RTCQualityDevice, &tv);
perhapsSetRTC(ourQuality, &tv);
#endif
powerMonInit();

View File

@@ -96,6 +96,8 @@ class Channels
bool setDefaultPresetCryptoForHash(ChannelHash channelHash);
int16_t getHash(ChannelIndex i) { return hashes[i]; }
private:
/** Given a channel index, change to use the crypto key specified by that index
*
@@ -113,8 +115,6 @@ class Channels
*/
int16_t generateHash(ChannelIndex channelNum);
int16_t getHash(ChannelIndex i) { return hashes[i]; }
/**
* Validate a channel, fixing any errors as needed
*/

View File

@@ -526,10 +526,6 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
#elif ARCH_PORTDUINO
if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) {
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
} else if (portduino_config.JSONFilename != "") {
if (portduino_config.JSONFilter == (_meshtastic_PortNum)0 || portduino_config.JSONFilter == p->decoded.portnum) {
JSONFile << MeshPacketSerializer::JsonSerialize(p, false) << std::endl;
}
}
#endif
return DecodeState::DECODE_SUCCESS;
@@ -692,7 +688,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
// Store a copy of encrypted packet for MQTT
DEBUG_HEAP_BEFORE;
meshtastic_MeshPacket *p_encrypted = packetPool.allocCopy(*p);
p_encrypted = packetPool.allocCopy(*p);
DEBUG_HEAP_AFTER("Router::handleReceived", p_encrypted);
// Take those raw bytes and convert them back into a well structured protobuf we can understand

View File

@@ -91,6 +91,9 @@ class Router : protected concurrency::OSThread, protected PacketHistory
before us */
uint32_t rxDupe = 0, txRelayCanceled = 0;
// pointer to the encrypted packet
meshtastic_MeshPacket *p_encrypted;
protected:
friend class RoutingModule;

View File

@@ -24,6 +24,9 @@ PB_BIND(meshtastic_Data, meshtastic_Data, 2)
PB_BIND(meshtastic_KeyVerification, meshtastic_KeyVerification, AUTO)
PB_BIND(meshtastic_StoreForwardPlusPlus, meshtastic_StoreForwardPlusPlus, 2)
PB_BIND(meshtastic_Waypoint, meshtastic_Waypoint, AUTO)
@@ -121,6 +124,8 @@ PB_BIND(meshtastic_ChunkedPayloadResponse, meshtastic_ChunkedPayloadResponse, AU

View File

@@ -478,6 +478,17 @@ typedef enum _meshtastic_Routing_Error {
meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED = 38
} meshtastic_Routing_Error;
/* enum message type? */
typedef enum _meshtastic_StoreForwardPlusPlus_SFPP_message_type {
/* message hash without chain hash means that no, it is not on the chain */
meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE = 0,
meshtastic_StoreForwardPlusPlus_SFPP_message_type_CHAIN_QUERY = 1,
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_REQUEST = 3,
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE = 4,
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_FIRSTHALF = 5,
meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF = 6
} meshtastic_StoreForwardPlusPlus_SFPP_message_type;
/* The priority of this message for sending.
Higher priorities are sent first (when managing the transmit queue).
This field is never sent over the air, it is only used internally inside of a local device node.
@@ -782,6 +793,24 @@ typedef struct _meshtastic_KeyVerification {
meshtastic_KeyVerification_hash2_t hash2;
} meshtastic_KeyVerification;
typedef PB_BYTES_ARRAY_T(32) meshtastic_StoreForwardPlusPlus_message_hash_t;
typedef PB_BYTES_ARRAY_T(32) meshtastic_StoreForwardPlusPlus_chain_hash_t;
typedef PB_BYTES_ARRAY_T(32) meshtastic_StoreForwardPlusPlus_root_hash_t;
typedef PB_BYTES_ARRAY_T(240) meshtastic_StoreForwardPlusPlus_message_t;
/* The actual over-the-mesh message doing store and forward++ */
typedef struct _meshtastic_StoreForwardPlusPlus { /* */
meshtastic_StoreForwardPlusPlus_SFPP_message_type sfpp_message_type;
meshtastic_StoreForwardPlusPlus_message_hash_t message_hash;
meshtastic_StoreForwardPlusPlus_chain_hash_t chain_hash;
meshtastic_StoreForwardPlusPlus_root_hash_t root_hash;
/* encapsulated message to share (may be split in half) */
meshtastic_StoreForwardPlusPlus_message_t message;
uint32_t encapsulated_id;
uint32_t encapsulated_to;
uint32_t encapsulated_from;
uint32_t encapsulated_rxtime;
} meshtastic_StoreForwardPlusPlus;
/* Waypoint message, used to share arbitrary locations across the mesh */
typedef struct _meshtastic_Waypoint {
/* Id of the waypoint */
@@ -1310,6 +1339,10 @@ extern "C" {
#define _meshtastic_Routing_Error_MAX meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED
#define _meshtastic_Routing_Error_ARRAYSIZE ((meshtastic_Routing_Error)(meshtastic_Routing_Error_RATE_LIMIT_EXCEEDED+1))
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_MAX meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF
#define _meshtastic_StoreForwardPlusPlus_SFPP_message_type_ARRAYSIZE ((meshtastic_StoreForwardPlusPlus_SFPP_message_type)(meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE_SECONDHALF+1))
#define _meshtastic_MeshPacket_Priority_MIN meshtastic_MeshPacket_Priority_UNSET
#define _meshtastic_MeshPacket_Priority_MAX meshtastic_MeshPacket_Priority_MAX
#define _meshtastic_MeshPacket_Priority_ARRAYSIZE ((meshtastic_MeshPacket_Priority)(meshtastic_MeshPacket_Priority_MAX+1))
@@ -1338,6 +1371,8 @@ extern "C" {
#define meshtastic_Data_portnum_ENUMTYPE meshtastic_PortNum
#define meshtastic_StoreForwardPlusPlus_sfpp_message_type_ENUMTYPE meshtastic_StoreForwardPlusPlus_SFPP_message_type
#define meshtastic_MeshPacket_priority_ENUMTYPE meshtastic_MeshPacket_Priority
@@ -1380,6 +1415,7 @@ extern "C" {
#define meshtastic_Routing_init_default {0, {meshtastic_RouteDiscovery_init_default}}
#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
#define meshtastic_KeyVerification_init_default {0, {0, {0}}, {0, {0}}}
#define meshtastic_StoreForwardPlusPlus_init_default {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0}
#define meshtastic_Waypoint_init_default {0, false, 0, false, 0, 0, 0, "", "", 0}
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
@@ -1411,6 +1447,7 @@ extern "C" {
#define meshtastic_Routing_init_zero {0, {meshtastic_RouteDiscovery_init_zero}}
#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, 0}
#define meshtastic_KeyVerification_init_zero {0, {0, {0}}, {0, {0}}}
#define meshtastic_StoreForwardPlusPlus_init_zero {_meshtastic_StoreForwardPlusPlus_SFPP_message_type_MIN, {0, {0}}, {0, {0}}, {0, {0}}, {0, {0}}, 0, 0, 0, 0}
#define meshtastic_Waypoint_init_zero {0, false, 0, false, 0, 0, 0, "", "", 0}
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0, {0, {0}}, 0, 0, 0, 0, _meshtastic_MeshPacket_TransportMechanism_MIN}
@@ -1489,6 +1526,15 @@ extern "C" {
#define meshtastic_KeyVerification_nonce_tag 1
#define meshtastic_KeyVerification_hash1_tag 2
#define meshtastic_KeyVerification_hash2_tag 3
#define meshtastic_StoreForwardPlusPlus_sfpp_message_type_tag 1
#define meshtastic_StoreForwardPlusPlus_message_hash_tag 2
#define meshtastic_StoreForwardPlusPlus_chain_hash_tag 3
#define meshtastic_StoreForwardPlusPlus_root_hash_tag 4
#define meshtastic_StoreForwardPlusPlus_message_tag 5
#define meshtastic_StoreForwardPlusPlus_encapsulated_id_tag 6
#define meshtastic_StoreForwardPlusPlus_encapsulated_to_tag 7
#define meshtastic_StoreForwardPlusPlus_encapsulated_from_tag 8
#define meshtastic_StoreForwardPlusPlus_encapsulated_rxtime_tag 9
#define meshtastic_Waypoint_id_tag 1
#define meshtastic_Waypoint_latitude_i_tag 2
#define meshtastic_Waypoint_longitude_i_tag 3
@@ -1705,6 +1751,19 @@ X(a, STATIC, SINGULAR, BYTES, hash2, 3)
#define meshtastic_KeyVerification_CALLBACK NULL
#define meshtastic_KeyVerification_DEFAULT NULL
#define meshtastic_StoreForwardPlusPlus_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, sfpp_message_type, 1) \
X(a, STATIC, SINGULAR, BYTES, message_hash, 2) \
X(a, STATIC, SINGULAR, BYTES, chain_hash, 3) \
X(a, STATIC, SINGULAR, BYTES, root_hash, 4) \
X(a, STATIC, SINGULAR, BYTES, message, 5) \
X(a, STATIC, SINGULAR, UINT32, encapsulated_id, 6) \
X(a, STATIC, SINGULAR, UINT32, encapsulated_to, 7) \
X(a, STATIC, SINGULAR, UINT32, encapsulated_from, 8) \
X(a, STATIC, SINGULAR, UINT32, encapsulated_rxtime, 9)
#define meshtastic_StoreForwardPlusPlus_CALLBACK NULL
#define meshtastic_StoreForwardPlusPlus_DEFAULT NULL
#define meshtastic_Waypoint_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, id, 1) \
X(a, STATIC, OPTIONAL, SFIXED32, latitude_i, 2) \
@@ -1980,6 +2039,7 @@ extern const pb_msgdesc_t meshtastic_RouteDiscovery_msg;
extern const pb_msgdesc_t meshtastic_Routing_msg;
extern const pb_msgdesc_t meshtastic_Data_msg;
extern const pb_msgdesc_t meshtastic_KeyVerification_msg;
extern const pb_msgdesc_t meshtastic_StoreForwardPlusPlus_msg;
extern const pb_msgdesc_t meshtastic_Waypoint_msg;
extern const pb_msgdesc_t meshtastic_MqttClientProxyMessage_msg;
extern const pb_msgdesc_t meshtastic_MeshPacket_msg;
@@ -2013,6 +2073,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_Routing_fields &meshtastic_Routing_msg
#define meshtastic_Data_fields &meshtastic_Data_msg
#define meshtastic_KeyVerification_fields &meshtastic_KeyVerification_msg
#define meshtastic_StoreForwardPlusPlus_fields &meshtastic_StoreForwardPlusPlus_msg
#define meshtastic_Waypoint_fields &meshtastic_Waypoint_msg
#define meshtastic_MqttClientProxyMessage_fields &meshtastic_MqttClientProxyMessage_msg
#define meshtastic_MeshPacket_fields &meshtastic_MeshPacket_msg
@@ -2069,6 +2130,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_QueueStatus_size 23
#define meshtastic_RouteDiscovery_size 256
#define meshtastic_Routing_size 259
#define meshtastic_StoreForwardPlusPlus_size 371
#define meshtastic_ToRadio_size 504
#define meshtastic_User_size 115
#define meshtastic_Waypoint_size 165

View File

@@ -86,6 +86,9 @@ typedef enum _meshtastic_PortNum {
/* Paxcounter lib included in the firmware
ENCODING: protobuf */
meshtastic_PortNum_PAXCOUNTER_APP = 34,
/* Store and Forward++ module included in the firmware
ENCODING: protobuf */
meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP = 35,
/* Provides a hardware serial interface to send and receive from the Meshtastic network.
Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic
network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network.

View File

@@ -61,6 +61,7 @@
#if ARCH_PORTDUINO
#include "input/LinuxInputImpl.h"
#include "input/SeesawRotary.h"
#include "modules/Native/StoreForwardPlusPlus.h"
#include "modules/Telemetry/HostMetrics.h"
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
#include "modules/StoreForwardModule.h"
@@ -243,6 +244,7 @@ void setupModules()
#endif
#if ARCH_PORTDUINO
new HostMetricsModule();
new StoreForwardPlusPlusModule();
#endif
#if HAS_TELEMETRY
new DeviceTelemetryModule();

View File

@@ -0,0 +1,653 @@
// create second module for satellites?
// The central node will generate a 256 or 128 bit value as its seed. This is the value other nodes subscribe to, and serves as
// the root of the chain
//
// Basic design:
// This module watches a channel for text messages.
// each message gets sha256 summed, and then appended to a git-style blockchain. Probably need a counter, too
// then the message, metadata, hash, and git hash information are saved. sqlite?
// nodes/sub-controllers can subscribe to a database
// A node can DM the controller, querying if a single message is on the chain, or asking for the last message hash
// if the message is not on the chain, the node can resend the message
// if the node lacks messages, it can request them
// will need the concept of sub-controllers, that subscribe to the central controller, can help push updates
// catch-up messages only go out when the mesh is low use %
// Normal firmware will only attempt to sync the chain a few times, then just ask for the latest few messages. A phone app can try
// harder
// host will periodically advertise its presence
// at least initially, there can only be one authoritative host
// first draft is just to save channel 0 in a git-style database
// message objects get a hash value
// the message chain gets a commit hash
//
#include "StoreForwardPlusPlus.h"
#include "MeshService.h"
#include "RTC.h"
#include "SHA256.h"
#include "meshUtils.h"
StoreForwardPlusPlusModule::StoreForwardPlusPlusModule()
: ProtobufModule("StoreForwardpp", meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP, &meshtastic_StoreForwardPlusPlus_msg),
concurrency::OSThread("StoreForwardpp")
{
LOG_WARN("StoreForwardPlusPlusModule init");
if (portduino_config.sfpp_stratum0)
LOG_WARN("SF++ stratum0");
int res = sqlite3_open("test.db", &ppDb);
LOG_WARN("Result1 %u", res);
char *err = nullptr;
res = sqlite3_exec(ppDb, " \
CREATE TABLE channel_messages( \
destination INT NOT NULL, \
sender INT NOT NULL, \
packet_id INT NOT NULL, \
want_ack BOOL NOT NULL, \
channel_hash INT NOT NULL, \
encrypted_bytes BLOB NOT NULL, \
message_hash BLOB NOT NULL, \
rx_time INT NOT NULL, \
commit_hash BLOB NOT NULL, \
payload TEXT, \
PRIMARY KEY (message_hash) \
);",
NULL, NULL, &err);
LOG_WARN("Result2 %u", res);
if (err != nullptr)
;
LOG_ERROR("%s", err);
sqlite3_free(err);
// create table DMs
res = sqlite3_exec(ppDb, " \
CREATE TABLE direct_messages( \
destination INT NOT NULL, \
sender INT NOT NULL, \
packet_id INT NOT NULL, \
want_ack BOOL NOT NULL, \
channel_hash INT NOT NULL, \
commit_hash BLOB NOT NULL, \
encrypted_bytes BLOB NOT NULL, \
message_hash BLOB NOT NULL, \
payload TEXT, \
rx_time INT NOT NULL, \
PRIMARY KEY (message_hash) \
);",
NULL, NULL, &err);
LOG_WARN("Result2 %u", res);
if (err != nullptr)
;
LOG_ERROR("%s", err);
sqlite3_free(err);
// create table mappings
// create table DMs
res = sqlite3_exec(ppDb, " \
CREATE TABLE mappings( \
chain_type INT NOT NULL, \
identifier INT NOT NULL, \
root_hash BLOB NOT NULL, \
PRIMARY KEY (identifier) \
);",
NULL, NULL, &err);
LOG_WARN("Result2 %u", res);
if (err != nullptr)
;
LOG_ERROR("%s", err);
sqlite3_free(err);
// store schema version somewhere
std::string insert_statement = "INSERT INTO channel_messages (destination, sender, packet_id, want_ack, channel_hash, \
encrypted_bytes, message_hash, rx_time, commit_hash, payload) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
sqlite3_prepare_v2(ppDb, insert_statement.c_str(), insert_statement.length(), &stmt, NULL);
encryptedOk = true;
this->setInterval(15 * 1000);
}
int32_t StoreForwardPlusPlusModule::runOnce()
{
LOG_WARN("StoreForward++ runONce");
if (getRTCQuality() < RTCQualityNTP) {
LOG_WARN("StoreForward++ deferred due to time quality %u", getRTCQuality());
return 60 * 60 * 1000;
}
uint8_t root_hash_bytes[32] = {0};
ChannelHash hash = channels.getHash(0);
getOrAddRootFromChannelHash(hash, root_hash_bytes);
// get tip of chain for this channel
uint8_t last_message_chain_hash[32] = {0};
uint8_t last_message_hash[32] = {0};
LOG_WARN("here5");
if (!getChainEnd(hash, last_message_chain_hash, last_message_hash)) {
LOG_WARN("Store and Forward++ database lookup returned null");
return 60 * 60 * 1000;
}
meshtastic_StoreForwardPlusPlus storeforward = meshtastic_StoreForwardPlusPlus_init_zero;
storeforward.sfpp_message_type = meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE;
// set root hash
// set message hash
storeforward.message_hash.size = 32;
memcpy(storeforward.message_hash.bytes, last_message_hash, 32);
// set chain hash
storeforward.chain_hash.size = 32;
memcpy(storeforward.chain_hash.bytes, last_message_chain_hash, 32);
// set root hash
storeforward.root_hash.size = 32;
memcpy(storeforward.root_hash.bytes, root_hash_bytes, 32);
// storeforward.
meshtastic_MeshPacket *p = allocDataProtobuf(storeforward);
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
p->channel = 0;
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
return 60 * 60 * 1000;
}
bool StoreForwardPlusPlusModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_StoreForwardPlusPlus *t)
{
LOG_WARN("in handleReceivedProtobuf");
LOG_WARN("Sfp++ node %u is informing us of packet", mp.from);
printBytes("chain_hash ", t->chain_hash.bytes, t->chain_hash.size);
if (t->sfpp_message_type == meshtastic_StoreForwardPlusPlus_SFPP_message_type_CANON_ANNOUNCE) {
// check chain_hash.size
if (portduino_config.sfpp_stratum0) {
LOG_WARN("Received a CANON_ANNOUNCE while stratum 0");
} else {
uint8_t tmp_root_hash_bytes[32] = {0};
LOG_WARN("Received a CANON_ANNOUNCE");
if (getRootFromChannelHash(router->p_encrypted->channel, tmp_root_hash_bytes)) {
// we found the hash, check if it's the right one
if (memcmp(tmp_root_hash_bytes, t->root_hash.bytes, 32) != 0) {
LOG_WARN("Found root hash, and it doesn't match!");
return true;
}
} else {
// There's the possibility that
addRootToMappings(router->p_encrypted->channel, t->root_hash.bytes);
LOG_WARN("Adding root hash to mappings");
}
// get tip of chain for this channel
uint8_t last_message_chain_hash[32] = {0};
uint8_t last_message_hash[32] = {0};
// get chain tip
if (getChainEnd(router->p_encrypted->channel, last_message_chain_hash, last_message_hash)) {
if (memcmp(last_message_chain_hash, t->chain_hash.bytes, 32) == 0) {
LOG_WARN("End of chain matches!");
} else
("End of chain does not match!");
} else {
LOG_WARN("No Messages on this chain, request!");
requestNextMessage(t->root_hash.bytes, t->root_hash.bytes);
}
// compare to chain tip in incoming message
// if not found, request the next message
}
} else if (t->sfpp_message_type == meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_REQUEST) {
uint8_t next_chain_hash[32] = {0};
LOG_WARN("Received link request");
getNextHash(t->root_hash.bytes, t->chain_hash.bytes, next_chain_hash);
printBytes("next chain hash: ", next_chain_hash, 32);
broadcastLink(next_chain_hash, t->root_hash.bytes);
// if root and chain hashes are the same, grab the first message on the chain
// if different, get the message directly after.
// check if the root
} else if (t->sfpp_message_type == meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE) {
LOG_WARN("Link Provide received!");
ChannelHash _channel_hash = getChannelHashFromRoot(t->root_hash.bytes);
addToChain(t->encapsulated_to, t->encapsulated_from, t->encapsulated_id, false, _channel_hash, t->message.bytes,
t->message.size, t->message_hash.bytes, t->chain_hash.bytes, t->root_hash.bytes, t->encapsulated_rxtime, "",
0);
}
return true;
}
ProcessMessage StoreForwardPlusPlusModule::handleReceived(const meshtastic_MeshPacket &mp)
{
// To avoid terrible time problems, require NTP or GPS time
if (getRTCQuality() < RTCQualityNTP) {
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
// the sender+destination pair is an interesting unique id (though ordering) (smaller one goes first?)
// so messages with a unique pair become a chain
// These get a table
// message to broadcast get a chain per channel hash
// second table
// for now, channel messages are limited to decryptable
// limited to text messages
// create a unique-from-nodenums() class that returns a 64-bit value
SHA256 message_hash, chain_hash;
uint8_t message_hash_bytes[32] = {0};
uint8_t chain_hash_bytes[32] = {0};
uint8_t root_hash_bytes[32] = {0};
// For the moment, this is strictly LoRa
if (mp.transport_mechanism != meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA) {
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
// will eventually host DMs and other undecodable messages
if (mp.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
// refuse without valid time?
LOG_WARN("in handleReceived");
if (mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP && mp.to == NODENUM_BROADCAST) {
// do not include rxtime in the message hash. We want these to match when more then one node receives and compares notes.
// maybe include it in the commit hash
message_hash.reset();
message_hash.update(router->p_encrypted->encrypted.bytes, router->p_encrypted->encrypted.size);
message_hash.update(&mp.to, sizeof(mp.to));
message_hash.update(&mp.from, sizeof(mp.from));
message_hash.update(&mp.id, sizeof(mp.id));
message_hash.finalize(message_hash_bytes, 32);
sqlite3_stmt *checkDup;
sqlite3_prepare_v2(ppDb, "SELECT COUNT(*) from channel_messages where commit_hash=?", -1, &checkDup, NULL);
sqlite3_bind_blob(checkDup, 1, message_hash_bytes, 32, NULL);
sqlite3_step(checkDup);
int numberFound = sqlite3_column_int(checkDup, 0);
LOG_WARN("found %u times in db", numberFound);
if (numberFound != 0)
return ProcessMessage::CONTINUE;
if (!portduino_config.sfpp_stratum0) {
LOG_WARN("TODO: downstream collection of messages");
return ProcessMessage::CONTINUE;
}
// need to resolve the channel hash to the root hash
getRootFromChannelHash(router->p_encrypted->channel, root_hash_bytes);
std::string getEntry_string =
"select commit_hash from channel_messages where channel_hash=? order by rowid desc LIMIT 1;";
sqlite3_stmt *getEntry;
int rc = sqlite3_prepare_v2(ppDb, getEntry_string.c_str(), getEntry_string.size(), &getEntry, NULL);
sqlite3_bind_int(getEntry, 1, router->p_encrypted->channel);
sqlite3_step(getEntry);
// this is allocated by sqlite3 and will be deleted when finalize is called
uint8_t *last_message_hash = (uint8_t *)sqlite3_column_blob(getEntry, 0);
if (last_message_hash) {
printBytes("last message: 0x", last_message_hash, 32);
} else {
printBytes("new chain root: 0x", root_hash_bytes, 32);
}
// look for message_hash_bytes in db
// if found, we bail early
chain_hash.reset();
if (last_message_hash) {
chain_hash.update(last_message_hash, 32);
} else {
chain_hash.update(root_hash_bytes, 32);
}
chain_hash.update(message_hash_bytes, 32);
// message_hash.update(&mp.rx_time, sizeof(mp.rx_time));
chain_hash.finalize(chain_hash_bytes, 32);
sqlite3_finalize(getEntry);
// select HEX(commit_hash),HEX(channel_hash), payload, destination from channel_messages order by rowid desc;
// push a message into the local chain DB
addToChain(mp.to, mp.from, mp.id, mp.want_ack, router->p_encrypted->channel, router->p_encrypted->encrypted.bytes,
router->p_encrypted->encrypted.size, message_hash_bytes, chain_hash_bytes, root_hash_bytes, mp.rx_time,
(char *)mp.decoded.payload.bytes, mp.decoded.payload.size);
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
// one of the command messages
} else if (mp.decoded.portnum == meshtastic_PortNum_STORE_FORWARD_PLUSPLUS_APP) {
LOG_WARN("Got a STORE_FORWARD++ packet");
meshtastic_StoreForwardPlusPlus scratch;
pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_StoreForwardPlusPlus_fields, &scratch);
handleReceivedProtobuf(mp, &scratch);
// when we get an update this way, if the message isn't on the chain, this node hasn't seen it, and can rebroadcast.
return ProcessMessage::CONTINUE;
}
return ProcessMessage::CONTINUE;
}
bool StoreForwardPlusPlusModule::getRootFromChannelHash(ChannelHash _ch_hash, uint8_t *_root_hash)
{
bool found = false;
sqlite3_stmt *getHash;
int rc = sqlite3_prepare_v2(ppDb, "select root_hash from mappings where identifier=?;", -1, &getHash, NULL);
sqlite3_bind_int(getHash, 1, _ch_hash);
sqlite3_step(getHash);
uint8_t *tmp_root_hash = (uint8_t *)sqlite3_column_blob(getHash, 0);
if (tmp_root_hash) {
LOG_WARN("Found root hash!");
memcpy(_root_hash, tmp_root_hash, 32);
found = true;
}
sqlite3_finalize(getHash);
return found;
}
ChannelHash StoreForwardPlusPlusModule::getChannelHashFromRoot(uint8_t *_root_hash)
{
sqlite3_stmt *getHash;
int rc = sqlite3_prepare_v2(ppDb, "select identifier from mappings where root_hash=?;", -1, &getHash, NULL);
sqlite3_bind_blob(getHash, 1, _root_hash, 32, NULL);
sqlite3_step(getHash);
ChannelHash tmp_hash = (ChannelHash)sqlite3_column_int(getHash, 0);
sqlite3_finalize(getHash);
return tmp_hash;
}
// return code indicates newly created chain
bool StoreForwardPlusPlusModule::getOrAddRootFromChannelHash(ChannelHash _ch_hash, uint8_t *_root_hash)
{
LOG_WARN("getOrAddRootFromChannelHash()");
bool isNew = !getRootFromChannelHash(_ch_hash, _root_hash);
if (isNew) {
if (portduino_config.sfpp_stratum0) {
LOG_WARN("Generating Root hash!");
// generate root hash
SHA256 chain_hash;
chain_hash.update(&_ch_hash, sizeof(_ch_hash));
NodeNum ourNode = nodeDB->getNodeNum();
chain_hash.update(&ourNode, sizeof(ourNode));
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
chain_hash.update(&rtc_sec, sizeof(rtc_sec));
chain_hash.finalize(_root_hash, 32);
addRootToMappings(_ch_hash, _root_hash);
LOG_WARN("here4");
}
}
return isNew;
}
bool StoreForwardPlusPlusModule::addRootToMappings(ChannelHash _ch_hash, uint8_t *_root_hash)
{
LOG_WARN("addRootToMappings()");
printBytes("_root_hash", _root_hash, 32);
sqlite3_stmt *getHash;
// write to the table
int rc =
sqlite3_prepare_v2(ppDb, "INSERT INTO mappings (chain_type, identifier, root_hash) VALUES(?, ?, ?);", -1, &getHash, NULL);
LOG_WARN("%d", rc);
int type = chain_types::channel_chain;
// note, must be an int variable
sqlite3_bind_int(getHash, 1, type);
sqlite3_bind_int(getHash, 2, _ch_hash);
sqlite3_bind_blob(getHash, 3, _root_hash, 32, NULL);
LOG_WARN("here1");
// sqlite3_bind_int(getHash, 4, nodeToAdd);
rc = sqlite3_step(getHash);
LOG_WARN("here2 %u, %s", rc, sqlite3_errmsg(ppDb));
sqlite3_finalize(getHash);
LOG_WARN("here3");
return true;
}
bool StoreForwardPlusPlusModule::getChainEnd(ChannelHash _ch_hash, uint8_t *_chain_hash, uint8_t *_message_hash)
{
LOG_WARN("getChainEnd");
std::string getEntry_string =
"select commit_hash, message_hash from channel_messages where channel_hash=? order by rowid desc LIMIT 1;";
sqlite3_stmt *getEntry;
int rc = sqlite3_prepare_v2(ppDb, getEntry_string.c_str(), getEntry_string.size(), &getEntry, NULL);
sqlite3_bind_int(getEntry, 1, _ch_hash);
sqlite3_step(getEntry);
uint8_t *last_message_chain_hash = (uint8_t *)sqlite3_column_blob(getEntry, 0);
uint8_t *last_message_hash = (uint8_t *)sqlite3_column_blob(getEntry, 1);
if (last_message_chain_hash == nullptr || last_message_hash == nullptr) {
LOG_WARN("Store and Forward++ database lookup returned null");
sqlite3_finalize(getEntry);
return false;
}
memcpy(_chain_hash, last_message_chain_hash, 32);
memcpy(_message_hash, last_message_hash, 32);
sqlite3_finalize(getEntry);
return true;
}
void StoreForwardPlusPlusModule::requestNextMessage(uint8_t *_root_hash, uint8_t *_chain_hash)
{
meshtastic_StoreForwardPlusPlus storeforward = meshtastic_StoreForwardPlusPlus_init_zero;
storeforward.sfpp_message_type = meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_REQUEST;
// set root hash
// set chain hash
storeforward.chain_hash.size = 32;
memcpy(storeforward.chain_hash.bytes, _chain_hash, 32);
// set root hash
storeforward.root_hash.size = 32;
memcpy(storeforward.root_hash.bytes, _root_hash, 32);
// storeforward.
meshtastic_MeshPacket *p = allocDataProtobuf(storeforward);
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
p->channel = 0;
LOG_INFO("Send packet to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
}
bool StoreForwardPlusPlusModule::getNextHash(uint8_t *_root_hash, uint8_t *_chain_hash, uint8_t *next_chain_hash)
{
LOG_WARN("getNextHash");
ChannelHash _channel_hash = getChannelHashFromRoot(_root_hash);
LOG_WARN("_channel_hash %u", _channel_hash);
sqlite3_stmt *getHash;
int rc = sqlite3_prepare_v2(ppDb, "select commit_hash from channel_messages where channel_hash=? order by rowid ASC;", -1,
&getHash, NULL);
LOG_WARN("%d", rc);
if (rc != SQLITE_OK) {
LOG_WARN("here2 %u, %s", rc, sqlite3_errmsg(ppDb));
}
sqlite3_bind_int(getHash, 1, _channel_hash);
sqlite3_step(getHash);
bool found_hash = false;
// asking for the first entry on the chain
if (memcmp(_root_hash, _chain_hash, 32) == 0) {
uint8_t *tmp_chain_hash = (uint8_t *)sqlite3_column_blob(getHash, 0);
memcpy(next_chain_hash, tmp_chain_hash, 32);
found_hash = true;
} else {
uint8_t *tmp_chain_hash;
while (sqlite3_step(getHash) != SQLITE_DONE) {
tmp_chain_hash = (uint8_t *)sqlite3_column_blob(getHash, 0);
if (found_hash) {
memcpy(next_chain_hash, tmp_chain_hash, 32);
break;
}
if (memcmp(tmp_chain_hash, _chain_hash, 32) == 0)
found_hash = true;
}
}
sqlite3_finalize(getHash);
return found_hash;
}
bool StoreForwardPlusPlusModule::broadcastLink(uint8_t *_chain_hash, uint8_t *_root_hash)
{
sqlite3_stmt *getHash;
int rc = sqlite3_prepare_v2(ppDb, "select destination, sender, packet_id, encrypted_bytes, message_hash, rx_time \
from channel_messages where commit_hash=?;",
-1, &getHash, NULL);
LOG_WARN("%d", rc);
if (rc != SQLITE_OK) {
LOG_WARN("here2 %u, %s", rc, sqlite3_errmsg(ppDb));
}
sqlite3_bind_blob(getHash, 1, _chain_hash, 32, NULL);
sqlite3_step(getHash);
meshtastic_StoreForwardPlusPlus storeforward = meshtastic_StoreForwardPlusPlus_init_zero;
storeforward.sfpp_message_type = meshtastic_StoreForwardPlusPlus_SFPP_message_type_LINK_PROVIDE;
storeforward.encapsulated_to = sqlite3_column_int(getHash, 0);
storeforward.encapsulated_from = sqlite3_column_int(getHash, 1);
storeforward.encapsulated_id = sqlite3_column_int(getHash, 2);
uint8_t *_payload = (uint8_t *)sqlite3_column_blob(getHash, 3);
storeforward.message.size = sqlite3_column_bytes(getHash, 3);
memcpy(storeforward.message.bytes, _payload, storeforward.message.size);
uint8_t *_message_hash = (uint8_t *)sqlite3_column_blob(getHash, 4);
storeforward.message_hash.size = 32;
memcpy(storeforward.message_hash.bytes, _message_hash, storeforward.message_hash.size);
storeforward.encapsulated_rxtime = sqlite3_column_int(getHash, 5);
storeforward.chain_hash.size = 32;
memcpy(storeforward.chain_hash.bytes, _chain_hash, 32);
storeforward.root_hash.size = 32;
memcpy(storeforward.root_hash.bytes, _root_hash, 32);
sqlite3_finalize(getHash);
meshtastic_MeshPacket *p = allocDataProtobuf(storeforward);
p->to = NODENUM_BROADCAST;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
p->channel = 0;
LOG_INFO("Send link to mesh");
service->sendToMesh(p, RX_SRC_LOCAL, true);
return true;
}
bool StoreForwardPlusPlusModule::addToChain(uint32_t to, uint32_t from, uint32_t id, bool want_ack, ChannelHash channel_hash,
uint8_t *encrypted_bytes, size_t encrypted_len, uint8_t *_message_hash,
uint8_t *_chain_hash, uint8_t *_root_hash, uint32_t _rx_time, char *payload_bytes,
size_t payload_len)
{
// push a message into the local chain DB
// destination
sqlite3_bind_int(stmt, 1, to);
// sender
sqlite3_bind_int(stmt, 2, from);
// packet_id
sqlite3_bind_int(stmt, 3, id);
// want_ack
sqlite3_bind_int(stmt, 4, want_ack);
// channel_hash
sqlite3_bind_int(stmt, 5, channel_hash);
// encrypted_bytes
sqlite3_bind_blob(stmt, 6, encrypted_bytes, encrypted_len, NULL);
// message_hash
sqlite3_bind_blob(stmt, 7, _message_hash, 32, NULL);
// rx_time
sqlite3_bind_int(stmt, 8, _rx_time);
// commit_hash
sqlite3_bind_blob(stmt, 9, _chain_hash, 32, NULL);
// payload
sqlite3_bind_text(stmt, 10, payload_bytes, payload_len, NULL);
sqlite3_step(stmt);
sqlite3_reset(stmt);
return true;
}
// announce latest hash
// chain_end_announce
// check if hash is known
// hash_query
// request next message
// link_request
// send encapsulated message
// link_provide_whole
// link_provide_half1
// link_provide_half2
// onboard request message?
// get x from top?
// messages
// Given this chain root, do you have a packet that matches this message hash?
// responds with chain hash etc
// given this chain root, what is your last chain and message hash?
// given this chain root, what is your next message after this chain hash? (do we have an overhead problem here?) (blegh,
// fragmentation) (but also, trunking)
// broadcast on this chain root, here is my last chain hash
// consider third-order nodes
// I can't talk directly to strata, I can talk to a satellite. Inform sat of a message. Sat stores it as if had seen it locally,
// and pushes it to central
// message Eventually works out through chain
// sat can capture time of receipt
// terms:
// CANON
// stratum
// chain
// links on the chain

View File

@@ -0,0 +1,74 @@
#pragma once
#include "Channels.h"
#include "ProtobufModule.h"
#include "Router.h"
#include "SinglePortModule.h"
#include "sqlite3.h"
/**
* A simple example module that just replies with "Message received" to any message it receives.
*/
class StoreForwardPlusPlusModule : public ProtobufModule<meshtastic_StoreForwardPlusPlus>, private concurrency::OSThread
{
public:
/** Constructor
* name is for debugging output
*/
StoreForwardPlusPlusModule();
/*
-Override the wantPacket method.
*/
virtual bool wantPacket(const meshtastic_MeshPacket *p) override
{
// if encrypted but not too FFFF
// want
switch (p->decoded.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP:
case 35:
return true;
default:
return false;
}
}
protected:
/** Called to handle a particular incoming message
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
it
*/
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_StoreForwardPlusPlus *t) override;
virtual int32_t runOnce() override;
private:
sqlite3 *ppDb;
sqlite3_stmt *stmt;
// returns wasfound
bool getRootFromChannelHash(ChannelHash, uint8_t *);
ChannelHash getChannelHashFromRoot(uint8_t *_root_hash);
bool getNextHash(uint8_t *_root_hash, uint8_t *_chain_hash, uint8_t *next_chain_hash);
// returns isnew
bool getOrAddRootFromChannelHash(ChannelHash, uint8_t *);
bool addRootToMappings(ChannelHash, uint8_t *);
// return indicates message found
bool getChainEnd(ChannelHash, uint8_t *, uint8_t *);
void requestNextMessage(uint8_t *, uint8_t *);
bool broadcastLink(uint8_t *_chain_hash, uint8_t *_root_hash);
bool addToChain(uint32_t, uint32_t, uint32_t, bool, ChannelHash, uint8_t *, size_t, uint8_t *, uint8_t *, uint8_t *, uint32_t,
char *, size_t);
enum chain_types {
channel_chain = 0,
};
};

View File

@@ -0,0 +1,235 @@
// create second module for satellites?
// The central node will generate a 256 or 128 bit value as its seed. This is the value other nodes subscribe to, and serves as
// the root of the chain
//
// Basic design:
// This module watches a channel for text messages.
// each message gets sha256 summed, and then appended to a git-style blockchain. Probably need a counter, too
// then the message, metadata, hash, and git hash information are saved. sqlite?
// nodes/sub-controllers can subscribe to a database
// A node can DM the controller, querying if a single message is on the chain, or asking for the last message hash
// if the message is not on the chain, the node can resend the message
// if the node lacks messages, it can request them
// will need the concept of sub-controllers, that subscribe to the central controller, can help push updates
// catch-up messages only go out when the mesh is low use %
// Normal firmware will only attempt to sync the chain a few times, then just ask for the latest few messages. A phone app can try
// harder
// host will periodically advertise its presence
// at least initially, there can only be one authoritative host
// first draft is just to save channel 0 in a git-style database
// message objects get a hash value
// the message chain gets a commit hash
//
#include "StoreForwardPlusPlusSat.h"
#include "SHA256.h"
#include "meshUtils.h"
StoreForwardPlusPlusSatModule::StoreForwardPlusPlusSatModule() : SinglePortModule("StoreForwardPlusPlus", (_meshtastic_PortNum)35)
{
LOG_WARN("StoreForwardPlusPlusSatModule init");
int res = sqlite3_open("test.db", &ppDb);
LOG_WARN("Result1 %u", res);
char *err = nullptr;
res = sqlite3_exec(ppDb, " \
CREATE TABLE channel_messages( \
destination INT NOT NULL, \
sender INT NOT NULL, \
packet_id INT NOT NULL, \
want_ack BOOL NOT NULL, \
channel_hash INT NOT NULL, \
encrypted_bytes BLOB NOT NULL, \
message_hash BLOB NOT NULL, \
rx_time INT NOT NULL, \
commit_hash BLOB NOT NULL, \
payload TEXT, \
PRIMARY KEY (message_hash) \
);",
NULL, NULL, &err);
LOG_WARN("Result2 %u", res);
if (err != nullptr)
;
LOG_ERROR("%s", err);
sqlite3_free(err);
// create table DMs
res = sqlite3_exec(ppDb, " \
CREATE TABLE direct_messages( \
destination INT NOT NULL, \
sender INT NOT NULL, \
packet_id INT NOT NULL, \
want_ack BOOL NOT NULL, \
channel_hash INT NOT NULL, \
commit_hash BLOB NOT NULL, \
encrypted_bytes BLOB NOT NULL, \
message_hash BLOB NOT NULL, \
payload TEXT, \
rx_time INT NOT NULL, \
PRIMARY KEY (message_hash) \
);",
NULL, NULL, &err);
LOG_WARN("Result2 %u", res);
if (err != nullptr)
;
LOG_ERROR("%s", err);
sqlite3_free(err);
// create table mappings
// create table DMs
res = sqlite3_exec(ppDb, " \
CREATE TABLE mappings( \
chain_type INT NOT NULL, \
identifier INT NOT NULL, \
root_hash BLOB NOT NULL, \
PRIMARY KEY (identifier) \
);",
NULL, NULL, &err);
LOG_WARN("Result2 %u", res);
if (err != nullptr)
;
LOG_ERROR("%s", err);
sqlite3_free(err);
// type
// sha256hash
// channelhash or 64 bit combination
// The sat version needs a scratch database of messages that have not been checked in
// store schema version somewhere
std::string insert_statement = "INSERT INTO channel_messages (destination, sender, packet_id, want_ack, channel_hash, \
encrypted_bytes, message_hash, rx_time, commit_hash, payload) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?);";
sqlite3_prepare(ppDb, insert_statement.c_str(), insert_statement.length(), &stmt, NULL);
encryptedOk = true;
}
ProcessMessage StoreForwardPlusPlusSatModule::handleReceived(const meshtastic_MeshPacket &mp)
{
// the sender+destination pair is an interesting unique id (though ordering) (smaller one goes first?)
// so messages with a unique pair become a chain
// These get a table
// message to broadcast get a chain per channel hash
// second table
// for now, channel messages are limited to decryptable
// limited to text messages
// create a unique-from-nodenums() class that returns a 64-bit value
SHA256 message_hash, chain_hash;
uint8_t message_hash_bytes[32] = {0};
uint8_t chain_hash_bytes[32] = {0};
// For the moment, this is strictly LoRa
if (mp.transport_mechanism != meshtastic_MeshPacket_TransportMechanism_TRANSPORT_LORA) {
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
// will eventually host DMs and other undecodable messages
if (mp.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
// refuse without valid time?
LOG_WARN("in handleReceived");
std::string getEntry_string = "select commit_hash from channel_messages where channel_hash=? order by rowid desc LIMIT 1;";
sqlite3_stmt *getEntry;
int rc = sqlite3_prepare(ppDb, getEntry_string.c_str(), getEntry_string.size(), &getEntry, NULL);
sqlite3_bind_int(getEntry, 1, router->p_encrypted->channel);
sqlite3_step(getEntry);
// this is allocated by sqlite3 and will be deleted when finalize is called
uint8_t *last_message_hash = (uint8_t *)sqlite3_column_blob(getEntry, 0);
if (last_message_hash) {
printBytes("last message: 0x", last_message_hash, 32);
} else {
// generate root hash and populate lookup table
}
// do not include rxtime in the message hash. We want these to match when more then one node receives and compares notes.
// feel free to include it in the commit hash
message_hash.reset();
message_hash.update(router->p_encrypted->encrypted.bytes, router->p_encrypted->encrypted.size);
message_hash.update(&mp.to, sizeof(mp.to));
message_hash.update(&mp.from, sizeof(mp.from));
message_hash.update(&mp.id, sizeof(mp.id));
message_hash.finalize(message_hash_bytes, 32);
chain_hash.reset();
if (last_message_hash) {
chain_hash.update(last_message_hash, 32);
}
chain_hash.update(message_hash_bytes, 32);
// message_hash.update(&mp.rx_time, sizeof(mp.rx_time));
chain_hash.finalize(chain_hash_bytes, 32);
sqlite3_finalize(getEntry);
// select HEX(commit_hash),HEX(channel_hash), payload, destination from channel_messages order by rowid desc;
// push a message into the local chain DB
// destination
sqlite3_bind_int(stmt, 1, mp.to);
// sender
sqlite3_bind_int(stmt, 2, mp.from);
// packet_id
sqlite3_bind_int(stmt, 3, mp.id);
// want_ack
sqlite3_bind_int(stmt, 4, mp.want_ack);
// channel_hash
sqlite3_bind_int(stmt, 5, router->p_encrypted->channel);
// encrypted_bytes
sqlite3_bind_blob(stmt, 6, router->p_encrypted->encrypted.bytes, router->p_encrypted->encrypted.size, NULL);
// message_hash
sqlite3_bind_blob(stmt, 7, message_hash_bytes, 32, NULL);
// rx_time
sqlite3_bind_int(stmt, 8, mp.rx_time);
// commit_hash
sqlite3_bind_blob(stmt, 9, chain_hash_bytes, 32, NULL);
// payload
sqlite3_bind_text(stmt, 10, (char *)mp.decoded.payload.bytes, mp.decoded.payload.size, NULL);
sqlite3_step(stmt);
sqlite3_reset(stmt);
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
// when we get an update this way, if the message isn't on the chain or in the scratch table, this node hasn't seen it, and can
// rebroadcast.
// messages
// Given this chain root, do you have a packet that matches this message hash?
// responds with chain hash etc
// given this chain root, what is your last chain and message hash?
// given this chain root, what is your next message after this chain hash? (do we have an overhead problem here?) (blegh,
// fragmentation) (but also, trunking)
// broadcast on this chain root, here is my last chain hash
// consider third-order nodes
// I can't talk directly to strata, I can talk to a satellite. Inform sat of a message. Sat stores it as if had seen it locally,
// and pushes it to central
// message Eventually works out through chain
// sat can capture time of receipt

View File

@@ -0,0 +1,41 @@
#pragma once
#include "Router.h"
#include "SinglePortModule.h"
#include "sqlite3.h"
/**
* A simple example module that just replies with "Message received" to any message it receives.
*/
class StoreForwardPlusPlusSatModule : public SinglePortModule // should probably derive this from the main class
{
public:
/** Constructor
* name is for debugging output
*/
StoreForwardPlusPlusSatModule();
/*
-Override the wantPacket method.
*/
virtual bool wantPacket(const meshtastic_MeshPacket *p) override
{
switch (p->decoded.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP:
case 35:
return true;
default:
return false;
}
}
protected:
/** Called to handle a particular incoming message
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
it
*/
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
private:
sqlite3 *ppDb;
sqlite3_stmt *stmt;
};

View File

@@ -29,7 +29,6 @@
portduino_config_struct portduino_config;
std::ofstream traceFile;
std::ofstream JSONFile;
Ch341Hal *ch341Hal = nullptr;
char *configPath = nullptr;
char *optionMac = nullptr;
@@ -464,7 +463,6 @@ void portduinoSetup()
if (portduino_config.lora_spi_dev != "" && portduino_config.lora_spi_dev != "ch341") {
SPI.begin(portduino_config.lora_spi_dev.c_str());
}
if (portduino_config.traceFilename != "") {
try {
traceFile.open(portduino_config.traceFilename, std::ios::out | std::ios::app);
@@ -472,21 +470,6 @@ void portduinoSetup()
std::cout << "*** traceFile Exception " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
if (!traceFile.is_open()) {
std::cout << "*** traceFile open failure" << std::endl;
exit(EXIT_FAILURE);
}
} else if (portduino_config.JSONFilename != "") {
try {
JSONFile.open(portduino_config.JSONFilename, std::ios::out | std::ios::app);
} catch (std::ofstream::failure &e) {
std::cout << "*** JSONFile Exception " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
if (!JSONFile.is_open()) {
std::cout << "*** JSONFile open failure" << std::endl;
exit(EXIT_FAILURE);
}
}
if (verboseEnabled && portduino_config.logoutputlevel != level_trace) {
portduino_config.logoutputlevel = level_debug;
@@ -534,29 +517,6 @@ bool loadConfig(const char *configPath)
portduino_config.logoutputlevel = level_error;
}
portduino_config.traceFilename = yamlConfig["Logging"]["TraceFile"].as<std::string>("");
portduino_config.JSONFilename = yamlConfig["Logging"]["JSONFile"].as<std::string>("");
portduino_config.JSONFilter = (_meshtastic_PortNum)yamlConfig["Logging"]["JSONFilter"].as<int>(0);
if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "textmessage")
portduino_config.JSONFilter = meshtastic_PortNum_TEXT_MESSAGE_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "telemetry")
portduino_config.JSONFilter = meshtastic_PortNum_TELEMETRY_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "nodeinfo")
portduino_config.JSONFilter = meshtastic_PortNum_NODEINFO_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "position")
portduino_config.JSONFilter = meshtastic_PortNum_POSITION_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "waypoint")
portduino_config.JSONFilter = meshtastic_PortNum_WAYPOINT_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "neighborinfo")
portduino_config.JSONFilter = meshtastic_PortNum_NEIGHBORINFO_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "traceroute")
portduino_config.JSONFilter = meshtastic_PortNum_TRACEROUTE_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "detection")
portduino_config.JSONFilter = meshtastic_PortNum_DETECTION_SENSOR_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "paxcounter")
portduino_config.JSONFilter = meshtastic_PortNum_PAXCOUNTER_APP;
else if (yamlConfig["Logging"]["JSONFilter"].as<std::string>("") == "remotehardware")
portduino_config.JSONFilter = meshtastic_PortNum_REMOTE_HARDWARE_APP;
if (yamlConfig["Logging"]["AsciiLogs"]) {
// Default is !isatty(1) but can be set explicitly in config.yaml
portduino_config.ascii_logs = yamlConfig["Logging"]["AsciiLogs"].as<bool>();
@@ -786,6 +746,10 @@ bool loadConfig(const char *configPath)
}
}
if (yamlConfig["StoreAndForward"]) {
portduino_config.sfpp_stratum0 = (yamlConfig["StoreAndForward"]["Stratum0"]).as<bool>(false);
}
if (yamlConfig["General"]) {
portduino_config.MaxNodes = (yamlConfig["General"]["MaxNodes"]).as<int>(200);
portduino_config.maxtophone = (yamlConfig["General"]["MaxMessageQueue"]).as<int>(100);

View File

@@ -5,7 +5,6 @@
#include "LR11x0Interface.h"
#include "Module.h"
#include "mesh/generated/meshtastic/mesh.pb.h"
#include "platform/portduino/USBHal.h"
#include "yaml-cpp/yaml.h"
@@ -47,8 +46,6 @@ struct pinMapping {
};
extern std::ofstream traceFile;
extern std::ofstream JSONFile;
extern Ch341Hal *ch341Hal;
int initGPIOPin(int pinNum, std::string gpioChipname, int line);
bool loadConfig(const char *configPath);
@@ -151,9 +148,6 @@ extern struct portduino_config_struct {
bool ascii_logs = !isatty(1);
bool ascii_logs_explicit = false;
std::string JSONFilename;
meshtastic_PortNum JSONFilter = (_meshtastic_PortNum)0;
// Webserver
std::string webserver_root_path = "";
std::string webserver_ssl_key_path = "/etc/meshtasticd/ssl/private_key.pem";
@@ -169,6 +163,9 @@ extern struct portduino_config_struct {
int configDisplayMode = 0;
bool has_configDisplayMode = false;
// Store and Forward++
bool sfpp_stratum0 = false;
// General
std::string mac_address = "";
bool mac_address_explicit = false;
@@ -419,29 +416,6 @@ extern struct portduino_config_struct {
}
if (traceFilename != "")
out << YAML::Key << "TraceFile" << YAML::Value << traceFilename;
if (JSONFilename != "") {
out << YAML::Key << "JSONFile" << YAML::Value << JSONFilename;
if (JSONFilter == meshtastic_PortNum_TEXT_MESSAGE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "textmessage";
else if (JSONFilter == meshtastic_PortNum_TELEMETRY_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "telemetry";
else if (JSONFilter == meshtastic_PortNum_NODEINFO_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "nodeinfo";
else if (JSONFilter == meshtastic_PortNum_POSITION_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "position";
else if (JSONFilter == meshtastic_PortNum_WAYPOINT_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "waypoint";
else if (JSONFilter == meshtastic_PortNum_NEIGHBORINFO_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "neighborinfo";
else if (JSONFilter == meshtastic_PortNum_TRACEROUTE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "traceroute";
else if (JSONFilter == meshtastic_PortNum_DETECTION_SENSOR_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "detection";
else if (JSONFilter == meshtastic_PortNum_PAXCOUNTER_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "paxcounter";
else if (JSONFilter == meshtastic_PortNum_REMOTE_HARDWARE_APP)
out << YAML::Key << "JSONFilter" << YAML::Value << "remotehardware";
}
if (ascii_logs_explicit) {
out << YAML::Key << "AsciiLogs" << YAML::Value << ascii_logs;
}

View File

@@ -605,13 +605,12 @@ void test_receiveAcksOwnSentMessages(void)
unitTest->publish(&p, nodeDB->getNodeId().c_str());
// FIXME: Better assertion for this test
// TEST_ASSERT_TRUE(mockRouter->packets_.empty());
// TEST_ASSERT_EQUAL(1, mockRoutingModule->ackNacks_.size());
// const auto &[err, to, idFrom, chIndex, hopLimit] = mockRoutingModule->ackNacks_.front();
// TEST_ASSERT_EQUAL(meshtastic_Routing_Error_NONE, err);
// TEST_ASSERT_EQUAL(myNodeInfo.my_node_num, to);
// TEST_ASSERT_EQUAL(p.id, idFrom);
TEST_ASSERT_TRUE(mockRouter->packets_.empty());
TEST_ASSERT_EQUAL(1, mockRoutingModule->ackNacks_.size());
const auto &[err, to, idFrom, chIndex, hopLimit] = mockRoutingModule->ackNacks_.front();
TEST_ASSERT_EQUAL(meshtastic_Routing_Error_NONE, err);
TEST_ASSERT_EQUAL(myNodeInfo.my_node_num, to);
TEST_ASSERT_EQUAL(p.id, idFrom);
}
// Should ignore our own messages from MQTT that were heard by other nodes.

View File

@@ -2,7 +2,6 @@
[env:meshtastic-dr-dev]
extends = esp32_base
board = esp32doit-devkit-v1
board_level = extra
board_upload.maximum_size = 4194304
board_upload.maximum_ram_size = 532480
build_flags =

View File

@@ -59,7 +59,7 @@ lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master
https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
h2zero/NimBLE-Arduino@2.3.7
h2zero/NimBLE-Arduino@^1.4.3
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib

View File

@@ -2,7 +2,7 @@
[env:tbeam]
extends = esp32_base
board = ttgo-t-beam
board_level = extra
board_level = pr
board_check = true
lib_deps = ${esp32_base.lib_deps}
build_flags = ${esp32_base.build_flags}

View File

@@ -1,7 +1,6 @@
[env:link32-s3-v1]
extends = esp32s3_base
board = esp32-s3-devkitc-1
board_level = extra
build_flags =
${esp32_base.build_flags}
-D LINK_32

View File

@@ -45,6 +45,7 @@ build_flags = ${native_base.build_flags} -Os -lX11 -linput -lxkbcommon -ffunctio
!pkg-config --libs openssl --silence-errors || :
!pkg-config --cflags --libs sdl2 --silence-errors || :
!pkg-config --cflags --libs libbsd-overlay --silence-errors || :
!pkg-config --cflags --libs sqlite3 --silence-errors || :
build_src_filter =
${native_base.build_src_filter}

View File

@@ -63,20 +63,9 @@ void initVariant()
// called from main-nrf52.cpp during the cpuDeepSleep() function
void variant_shutdown()
{
digitalWrite(red_LED_PIN, HIGH);
digitalWrite(green_LED_PIN, HIGH);
digitalWrite(LED_BLUE, HIGH);
digitalWrite(PIN_EN1, LOW);
digitalWrite(PIN_EN2, LOW);
digitalWrite(EEPROM_POWER, LOW);
digitalWrite(KEY_POWER, LOW);
digitalWrite(DHT_POWER, LOW);
digitalWrite(ACC_POWER, LOW);
digitalWrite(Battery_POWER, LOW);
digitalWrite(GPS_POWER, LOW);
// This sets the pin to OUTPUT and LOW for the pins *not* in the if block.
for (int pin = 0; pin < 48; pin++) {
if (pin == PIN_POWER_USB || pin == BUTTON_PIN || pin == PIN_EN1 || pin == PIN_EN2 || pin == DHT_POWER ||
pin == ACC_POWER || pin == Battery_POWER || pin == GPS_POWER || pin == LR1110_SPI_MISO_PIN ||

View File

@@ -19,6 +19,7 @@ build_type = release
build_flags =
-include variants/nrf52840/cpp_overrides/lfs_util.h
${arduino_base.build_flags}
-DSERIAL_BUFFER_SIZE=1024
-Wno-unused-variable
-Isrc/platform/nrf52
-DLFS_NO_ASSERT ; Disable LFS assertions , see https://github.com/meshtastic/firmware/pull/3818

View File

@@ -1,9 +1,7 @@
[nrf52832_base]
extends = nrf52_base
build_flags =
${nrf52_base.build_flags}
-DSERIAL_BUFFER_SIZE=1024
build_flags = ${nrf52_base.build_flags}
lib_deps =
${nrf52_base.lib_deps}

View File

@@ -1,9 +1,7 @@
[nrf52840_base]
extends = nrf52_base
build_flags =
${nrf52_base.build_flags}
-DSERIAL_BUFFER_SIZE=4096
build_flags = ${nrf52_base.build_flags}
lib_deps =
${nrf52_base.lib_deps}

View File

@@ -1,7 +1,6 @@
[env:feather_rp2040_rfm95]
extends = rp2040_base
board = adafruit_feather
board_level = extra
upload_protocol = picotool
# add our variants files to the include and src paths
build_flags =