Compare commits

..

5 Commits

Author SHA1 Message Date
Jonathan Bennett
0f0e704f29 Merge branch 'develop' into esp32-h2 2025-12-10 11:11:57 -06:00
Jonathan Bennett
23aaee737a Merge branch 'develop' into esp32-h2 2025-12-09 20:57:47 -06:00
Jonathan Bennett
42c46cad41 Merge branch 'develop' into esp32-h2 2025-12-08 16:52:00 -06:00
Jonathan Bennett
f0b72a4b4b Move the esp32-h2 .ini 2025-12-08 16:49:01 -06:00
Jonathan Bennett
24ca4602b1 roughed in support for esp32-h2 via Waveshare dev board 2025-12-06 12:42:05 -06:00
44 changed files with 159 additions and 1237 deletions

View File

@@ -56,18 +56,16 @@ jobs:
ota_firmware_source: ${{ steps.ota_dir.outputs.src || '' }}
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
- name: Job summary
- name: Echo manifest from release/firmware-*.mt.json to job summary
if: ${{ always() }}
env:
PIO_ENV: ${{ inputs.pio_env }}
run: |
echo "## $PIO_ENV" >> $GITHUB_STEP_SUMMARY
echo "<details><summary><strong>Manifest</strong></summary>" >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo "## Manifest: \`$PIO_ENV\`" >> $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@v5

View File

@@ -77,21 +77,16 @@ jobs:
fail-fast: false
matrix:
check: ${{ fromJson(needs.setup.outputs.check) }}
# Use 'arctastic' self-hosted runner pool when checking in the main repo
runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }}
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Build base
id: base
uses: ./.github/actions/setup-base
- name: Check ${{ matrix.check.board }}
uses: meshtastic/gh-action-firmware@main
with:
pio_platform: ${{ matrix.check.platform }}
pio_env: ${{ matrix.check.board }}
pio_target: check
run: bin/check-all.sh ${{ matrix.check.board }}
build:
needs: [setup, version]

View File

@@ -102,7 +102,7 @@ jobs:
PIP_DISABLE_PIP_VERSION_CHECK: 1
- name: Create Bumps pull request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
base: ${{ github.event.repository.default_branch }}
branch: create-pull-request/bump-version

View File

@@ -31,7 +31,7 @@ jobs:
./bin/regen-protos.sh
- name: Create pull request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
branch: create-pull-request/update-protobufs
labels: submodules

View File

@@ -9,24 +9,24 @@ plugins:
lint:
enabled:
- checkov@3.2.495
- renovate@42.42.2
- renovate@42.30.4
- prettier@3.7.4
- trufflehog@3.92.1
- trufflehog@3.91.2
- yamllint@1.37.1
- bandit@1.9.2
- trivy@0.68.1
- trivy@0.67.2
- taplo@0.10.0
- ruff@0.14.8
- ruff@0.14.7
- isort@7.0.0
- markdownlint@0.46.0
- oxipng@10.0.0
- oxipng@9.1.5
- svgo@4.0.0
- actionlint@1.7.9
- flake8@7.3.0
- hadolint@2.14.0
- shfmt@3.6.0
- shellcheck@0.11.0
- black@25.12.0
- black@25.11.0
- git-diff-check
- gitleaks@8.30.0
- clang-format@16.0.3

View File

@@ -22,7 +22,7 @@ export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION
pio run --environment $1 -t mtjson # -v
pio run --environment $1 # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
@@ -32,10 +32,20 @@ cp $BUILDDIR/$basename.factory.bin $OUTDIR/$basename.factory.bin
echo "Copying ESP32 update bin file"
cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin
echo "Copying Filesystem for ESP32 targets"
echo "Building Filesystem for ESP32 targets"
# If you want to build the webui, uncomment the following lines
# pio run --environment $1 -t buildfs
# cp .pio/build/$1/littlefs.bin $OUTDIR/littlefswebui-$1-$VERSION.bin
# # Remove webserver files from the filesystem and rebuild
# ls -l data/static # Diagnostic list of files
# rm -rf data/static
pio run --environment $1 -t buildfs --disable-auto-clean
cp $BUILDDIR/littlefs-$1-$VERSION.bin $OUTDIR/littlefs-$1-$VERSION.bin
cp bin/device-install.* $OUTDIR/
cp bin/device-update.* $OUTDIR/
echo "Copying manifest"
# Generate the manifest file
echo "Generating Meshtastic manifest"
TIMEFORMAT="Generated manifest in %E seconds"
time pio run --environment $1 -t mtjson --silent --disable-auto-clean
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -22,7 +22,7 @@ export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION
pio run --environment $1 -t mtjson # -v
pio run --environment $1 # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
@@ -47,5 +47,8 @@ if (echo $1 | grep -q "rak4631"); then
cp $SRCHEX $OUTDIR/
fi
echo "Copying manifest"
# Generate the manifest file
echo "Generating Meshtastic manifest"
TIMEFORMAT="Generated manifest in %E seconds"
time pio run --environment $1 -t mtjson --silent --disable-auto-clean
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -22,12 +22,15 @@ export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION
pio run --environment $1 -t mtjson # -v
pio run --environment $1 # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
echo "Copying uf2 file"
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2
echo "Copying manifest"
# Generate the manifest file
echo "Generating Meshtastic manifest"
TIMEFORMAT="Generated manifest in %E seconds"
time pio run --environment $1 -t mtjson --silent --disable-auto-clean
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -22,12 +22,15 @@ export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION
pio run --environment $1 -t mtjson # -v
pio run --environment $1 # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
echo "Copying STM32 bin file"
cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin
echo "Copying manifest"
# Generate the manifest file
echo "Generating Meshtastic manifest"
TIMEFORMAT="Generated manifest in %E seconds"
time pio run --environment $1 -t mtjson --silent --disable-auto-clean
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -159,22 +159,20 @@ def load_boot_logo(source, target, env):
# Load the boot logo on TFT builds
if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
env.AddPreAction(f"$BUILD_DIR/{lfsbin}", load_boot_logo)
env.AddPreAction('$BUILD_DIR/littlefs.bin', load_boot_logo)
mtjson_deps = ["buildprog"]
if platform.name == "espressif32":
# Build littlefs image as part of mtjson target
# Equivalent to `pio run -t buildfs`
target_lfs = env.DataToBin(
join("$BUILD_DIR", "${ESP32_FS_IMAGE_NAME}"), "$PROJECT_DATA_DIR"
)
mtjson_deps.append(target_lfs)
# Rename (mv) littlefs.bin to include the PROGNAME
# This ensures the littlefs.bin is named consistently with the firmware
env.AddPostAction('$BUILD_DIR/littlefs.bin', env.VerboseAction(
f'mv $BUILD_DIR/littlefs.bin $BUILD_DIR/{lfsbin}',
f'Renaming littlefs.bin to {lfsbin}'
))
env.AddCustomTarget(
name="mtjson",
dependencies=mtjson_deps,
dependencies=None,
actions=[manifest_gather],
title="Meshtastic Manifest",
description="Generating Meshtastic manifest JSON + Checksums",
always_build=False,
always_build=True,
)

View File

@@ -11,9 +11,6 @@ else:
prefsLoc = env["PROJECT_DIR"] + "/version.properties"
verObj = readProps(prefsLoc)
env.Replace(PROGNAME=f"firmware-{env.get('PIOENV')}-{verObj['long']}")
env.Replace(ESP32_FS_IMAGE_NAME=f"littlefs-{env.get('PIOENV')}-{verObj['long']}")
# Print the new program name for verification
print(f"PROGNAME: {env.get('PROGNAME')}")
if platform.name == "espressif32":
print(f"ESP32_FS_IMAGE_NAME: {env.get('ESP32_FS_IMAGE_NAME')}")

View File

@@ -10,12 +10,6 @@ Import("env")
platform = env.PioPlatform()
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
# IntelHex workaround, remove after fixed upstream
# https://github.com/platformio/platform-espressif32/issues/1632
try:
import intelhex
except ImportError:
env.Execute("$PYTHONEXE -m pip install intelhex")
import esptool

View File

@@ -532,10 +532,8 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
const int labelX = x;
int barsOffset = (isHighResolution) ? 24 : 0;
#ifdef USE_EINK
#ifndef T_DECK_PRO
barsOffset -= 12;
#endif
#endif
#if defined(M5STACK_UNITC6L)
const int barX = x + 45 + barsOffset;
#else
@@ -576,7 +574,7 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
#endif
// Value string
display->setTextAlignment(TEXT_ALIGN_RIGHT);
display->drawString(SCREEN_WIDTH, getTextPositions(display)[line], combinedStr);
display->drawString(SCREEN_WIDTH - 2, getTextPositions(display)[line], combinedStr);
};
// === Memory values ===

View File

@@ -428,17 +428,10 @@ 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(ourQuality, &tv);
perhapsSetRTC(RTCQualityDevice, &tv);
#endif
powerMonInit();

View File

@@ -96,8 +96,6 @@ 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
*
@@ -115,6 +113,8 @@ 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

@@ -225,4 +225,4 @@ class MeshModule
/** set the destination and packet parameters of packet p intended as a reply to a particular "to" packet
* This ensures that if the request packet was sent reliably, the reply is sent that way as well.
*/
void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to);
void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to);

View File

@@ -805,15 +805,11 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.output_ms = 500;
moduleConfig.external_notification.nag_timeout = 2;
#endif
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312) || defined(MUZI_BASE)
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312)
// Default to RAK led pin 2 (blue)
moduleConfig.external_notification.enabled = true;
moduleConfig.external_notification.output = PIN_LED2;
#if defined(MUZI_BASE)
moduleConfig.external_notification.active = false;
#else
moduleConfig.external_notification.active = true;
#endif
moduleConfig.external_notification.alert_message = true;
moduleConfig.external_notification.output_ms = 1000;
moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;

View File

@@ -150,9 +150,7 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
PacketId nakId = (c && c->error_reason != meshtastic_Routing_Error_NONE) ? p->decoded.request_id : 0;
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
if ((ackId || nakId) &&
// Implicit ACKs from MQTT should not stop retransmissions
!(isFromUs(p) && p->transport_mechanism == meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MQTT)) {
if (ackId || nakId) {
LOG_DEBUG("Received a %s for 0x%x, stopping retransmissions", ackId ? "ACK" : "NAK", ackId);
if (ackId) {
stopRetransmission(p->to, ackId);

View File

@@ -688,7 +688,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
// Store a copy of encrypted packet for MQTT
DEBUG_HEAP_BEFORE;
p_encrypted = packetPool.allocCopy(*p);
meshtastic_MeshPacket *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,9 +91,6 @@ 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,9 +24,6 @@ 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)
@@ -124,8 +121,6 @@ PB_BIND(meshtastic_ChunkedPayloadResponse, meshtastic_ChunkedPayloadResponse, AU

View File

@@ -478,17 +478,6 @@ 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.
@@ -793,24 +782,6 @@ 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 */
@@ -1339,10 +1310,6 @@ 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))
@@ -1371,8 +1338,6 @@ 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
@@ -1415,7 +1380,6 @@ 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}
@@ -1447,7 +1411,6 @@ 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}
@@ -1526,15 +1489,6 @@ 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
@@ -1751,19 +1705,6 @@ 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) \
@@ -2039,7 +1980,6 @@ 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;
@@ -2073,7 +2013,6 @@ 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
@@ -2130,7 +2069,6 @@ 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,9 +86,6 @@ 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

@@ -5,7 +5,8 @@
#include "configuration.h"
#include "input/InputBroker.h"
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(CONFIG_IDF_TARGET_ESP32C6)
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(CONFIG_IDF_TARGET_ESP32C6) && \
!defined(CONFIG_IDF_TARGET_ESP32H2)
#include <NonBlockingRtttl.h>
#else
// Noop class for portduino.

View File

@@ -61,7 +61,6 @@
#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"
@@ -218,7 +217,7 @@ void setupModules()
}
#endif // HAS_BUTTON
#if ARCH_PORTDUINO
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR && portduino_config.i2cdev != "") {
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
seesawRotary = new SeesawRotary("SeesawRotary");
if (!seesawRotary->init()) {
delete seesawRotary;
@@ -244,7 +243,6 @@ void setupModules()
#endif
#if ARCH_PORTDUINO
new HostMetricsModule();
new StoreForwardPlusPlusModule();
#endif
#if HAS_TELEMETRY
new DeviceTelemetryModule();

View File

@@ -1,653 +0,0 @@
// 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

@@ -1,74 +0,0 @@
#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

@@ -1,235 +0,0 @@
// 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

@@ -1,41 +0,0 @@
#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

@@ -45,12 +45,8 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
{
auto p = *pptr;
const auto transport = mp.transport_mechanism;
if (isFromUs(&mp) && !IS_ONE_OF(transport, meshtastic_MeshPacket_TransportMechanism_TRANSPORT_INTERNAL,
meshtastic_MeshPacket_TransportMechanism_TRANSPORT_API)) {
LOG_WARN("Ignoring packet supposedly from us over external transport");
return true;
}
// If inbound message is a replay (or spoof!) of our own messages, we shouldn't process
// (why use second-hand sources for our own data?)
// FIXME this can in fact happen with packets sent from EUD (src=RX_SRC_USER)
// to set fixed location, EUD-GPS location or just the time (see also issue #900)
@@ -476,53 +472,19 @@ void PositionModule::sendLostAndFoundText()
delete[] message;
}
// Helper: return imprecise (truncated + centered) lat/lon as int32 using current precision
static inline void computeImpreciseLatLon(int32_t inLat, int32_t inLon, uint8_t precisionBits, int32_t &outLat, int32_t &outLon)
{
if (precisionBits > 0 && precisionBits < 32) {
// Build mask for top 'precisionBits' bits of a 32-bit unsigned field
const uint32_t mask = (precisionBits == 32) ? UINT32_MAX : (UINT32_MAX << (32 - precisionBits));
// Note: latitude_i/longitude_i are stored as signed 32-bit in meshtastic code but
// the bitmask logic used previously operated as unsigned—preserve that behavior by
// casting to uint32_t for masking, then back to int32_t.
uint32_t lat_u = static_cast<uint32_t>(inLat) & mask;
uint32_t lon_u = static_cast<uint32_t>(inLon) & mask;
// Add the "center of cell" offset used elsewhere:
// The code previously added (1 << (31 - precision)) to produce the middle of the possible location.
uint32_t center_offset = (1u << (31 - precisionBits));
lat_u += center_offset;
lon_u += center_offset;
outLat = static_cast<int32_t>(lat_u);
outLon = static_cast<int32_t>(lon_u);
} else {
// full precision: return input unchanged
outLat = inLat;
outLon = inLon;
}
}
struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition)
{
// The minimum distance to travel before we are able to send a new position packet.
const uint32_t distanceTravelThreshold =
Default::getConfiguredOrDefault(config.position.broadcast_smart_minimum_distance, 100);
int32_t lastLatImprecise, lastLonImprecise;
int32_t currentLatImprecise, currentLonImprecise;
// Determine the distance in meters between two points on the globe
float distanceTraveledSinceLastSend = GeoCoord::latLongToMeter(
lastGpsLatitude * 1e-7, lastGpsLongitude * 1e-7, currentPosition.latitude_i * 1e-7, currentPosition.longitude_i * 1e-7);
computeImpreciseLatLon(lastGpsLatitude, lastGpsLongitude, precision, lastLatImprecise, lastLonImprecise);
computeImpreciseLatLon(currentPosition.latitude_i, currentPosition.longitude_i, precision, currentLatImprecise,
currentLonImprecise);
float distMeters = GeoCoord::latLongToMeter(lastLatImprecise * 1e-7, lastLonImprecise * 1e-7, currentLatImprecise * 1e-7,
currentLonImprecise * 1e-7);
float distanceTraveled = fabsf(distMeters);
return SmartPosition{.distanceTraveled = distanceTraveled,
return SmartPosition{.distanceTraveled = abs(distanceTraveledSinceLastSend),
.distanceThreshold = distanceTravelThreshold,
.hasTraveledOverThreshold = distanceTraveled >= distanceTravelThreshold};
.hasTraveledOverThreshold = abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold};
}
void PositionModule::handleNewPosition()

View File

@@ -75,12 +75,6 @@ uint8_t RoutingModule::getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit
return Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit); // Use the default hop limit
}
meshtastic_MeshPacket *RoutingModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex,
uint8_t hopLimit)
{
return MeshModule::allocAckNak(err, to, idFrom, chIndex, hopLimit);
}
RoutingModule::RoutingModule() : ProtobufModule("routing", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg)
{
isPromiscuous = true;

View File

@@ -16,9 +16,6 @@ class RoutingModule : public ProtobufModule<meshtastic_Routing>
virtual void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopLimit = 0,
bool ackWantsAck = false);
meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex,
uint8_t hopLimit = 0);
// Given the hopStart and hopLimit upon reception of a request, return the hop limit to use for the response
uint8_t getHopLimitForResponse(uint8_t hopStart, uint8_t hopLimit);

View File

@@ -71,7 +71,7 @@ SerialModule::SerialModule() : StreamAPI(&Serial), concurrency::OSThread("Serial
api_type = TYPE_SERIAL;
}
static Print *serialPrint = &Serial;
#elif defined(CONFIG_IDF_TARGET_ESP32C6) || defined(RAK3172) || defined(EBYTE_E77_MBL)
#elif defined(CONFIG_IDF_TARGET_ESP32C6) || defined(RAK3172) || defined(EBYTE_E77_MBL) || defined(CONFIG_IDF_TARGET_ESP32H2)
SerialModule::SerialModule() : StreamAPI(&Serial1), concurrency::OSThread("Serial")
{
api_type = TYPE_SERIAL;
@@ -175,7 +175,7 @@ int32_t SerialModule::runOnce()
// Give it a chance to flush out 💩
delay(10);
}
#if defined(CONFIG_IDF_TARGET_ESP32C6)
#if defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2)
if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
Serial1.setRxBufferSize(RX_BUFFER);
Serial1.begin(baud, SERIAL_8N1, moduleConfig.serial.rxd, moduleConfig.serial.txd);
@@ -277,7 +277,7 @@ int32_t SerialModule::runOnce()
}
#endif
else {
#if defined(CONFIG_IDF_TARGET_ESP32C6)
#if defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2)
while (Serial1.available()) {
serialPayloadSize = Serial1.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
#else
@@ -538,7 +538,7 @@ void SerialModule::processWXSerial()
{
#if !defined(TTGO_T_ECHO) && !defined(T_ECHO_LITE) && !defined(CANARYONE) && !defined(CONFIG_IDF_TARGET_ESP32C6) && \
!defined(MESHLINK) && !defined(ELECROW_ThinkNode_M1) && !defined(ELECROW_ThinkNode_M3) && !defined(ELECROW_ThinkNode_M5) && \
!defined(ARCH_STM32WL) && !defined(MUZI_BASE)
!defined(ARCH_STM32WL) && !defined(MUZI_BASE) && !defined(CONFIG_IDF_TARGET_ESP32H2)
static unsigned int lastAveraged = 0;
static unsigned int averageIntervalMillis = 300000; // 5 minutes hard coded.
static double dir_sum_sin = 0;

View File

@@ -87,13 +87,10 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
// Generate an implicit ACK towards ourselves (handled and processed only locally!) for this message.
// We do this because packets are not rebroadcasted back into MQTT anymore and we assume that at least one node
// receives it when we get our own packet back. Then we'll stop our retransmissions.
if (isFromUs(e.packet)) {
auto pAck = routingModule->allocAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index);
pAck->transport_mechanism = meshtastic_MeshPacket_TransportMechanism_TRANSPORT_MQTT;
router->sendLocal(pAck);
} else {
if (isFromUs(e.packet))
routingModule->sendAckNak(meshtastic_Routing_Error_NONE, getFrom(e.packet), e.packet->id, ch.index);
else
LOG_INFO("Ignore downlink message we originally sent");
}
return;
}
if (isFromUs(e.packet)) {

View File

@@ -169,6 +169,8 @@ void esp32Setup()
// #define APP_WATCHDOG_SECS 45
#define APP_WATCHDOG_SECS 90
// esp_task_wdt_init returns an unknown error, so skip it on ESP32H2
#ifndef CONFIG_IDF_TARGET_ESP32H2
#ifdef CONFIG_IDF_TARGET_ESP32C6
esp_task_wdt_config_t *wdt_config = (esp_task_wdt_config_t *)malloc(sizeof(esp_task_wdt_config_t));
wdt_config->timeout_ms = APP_WATCHDOG_SECS * 1000;
@@ -181,7 +183,7 @@ void esp32Setup()
#endif
res = esp_task_wdt_add(NULL);
assert(res == ESP_OK);
#endif
#if HAS_32768HZ
enableSlowCLK();
#endif
@@ -223,9 +225,10 @@ void cpuDeepSleep(uint32_t msecToWake)
13,
#endif
34, 35, 37};
#ifndef CONFIG_IDF_TARGET_ESP32H2
for (int i = 0; i < sizeof(rtcGpios); i++)
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
#endif
#endif
// FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using
@@ -258,10 +261,10 @@ void cpuDeepSleep(uint32_t msecToWake)
#endif // #end ESP32S3_WAKE_TYPE
#endif
#ifdef ESP_PD_DOMAIN_RTC_PERIPH
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#endif
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery)
}

View File

@@ -337,7 +337,7 @@ void cpuDeepSleep(uint32_t msecToWake)
#endif
#ifdef TTGO_T_ECHO
// To power off the T-Echo, the display must be set
// To power off the T-Echo, the display must be set
// as an input pin; otherwise, there will be leakage current.
pinMode(PIN_EINK_CS, INPUT);
pinMode(PIN_EINK_DC, INPUT);

View File

@@ -746,10 +746,6 @@ 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

@@ -163,9 +163,6 @@ 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;

View File

@@ -388,10 +388,10 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
uint64_t sleepUsec = sleepMsec * 1000LL;
// NOTE! ESP docs say we must disable bluetooth and wifi before light sleep
#ifdef ESP_PD_DOMAIN_RTC_PERIPH
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#endif
#if defined(BUTTON_PIN) && defined(BUTTON_NEED_PULLUP)
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
#endif
@@ -523,6 +523,8 @@ void enableModemSleep()
esp32_config.max_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ;
#elif CONFIG_IDF_TARGET_ESP32C6
esp32_config.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
#elif CONFIG_IDF_TARGET_ESP32H2
esp32_config.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
#elif CONFIG_IDF_TARGET_ESP32C3
esp32_config.max_freq_mhz = CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ;
#else

View File

@@ -5,7 +5,7 @@ custom_esp32_kind = esp32
custom_mtjson_part =
platform =
# renovate: datasource=custom.pio depName=platformio/espressif32 packageName=platformio/platform/espressif32
platformio/espressif32@6.12.0
platformio/espressif32@6.11.0
extra_scripts =
${env.extra_scripts}

View File

@@ -0,0 +1,47 @@
[esp32h2_base]
extends = esp32_base
platform =
# Do not renovate until we have switched to pioarduino tagged builds
https://github.com/Jason2866/platform-espressif32/archive/39475a7213fa3a52b0c2048d1a31c215fc85fcf8.zip
build_flags =
${arduino_base.build_flags}
-Wall
-Wextra
-Isrc/platform/esp32
-std=c++11
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
-DSERIAL_BUFFER_SIZE=4096
-DLIBPAX_ARDUINO
-DLIBPAX_WIFI
-DLIBPAX_BLE
-DMESHTASTIC_EXCLUDE_WEBSERVER
;-DDEBUG_HEAP
; TEMP
-DHAS_BLUETOOTH=0
-DMESHTASTIC_EXCLUDE_PAXCOUNTER
-DMESHTASTIC_EXCLUDE_BLUETOOTH
lib_deps =
${arduino_base.lib_deps}
${networking_base.lib_deps}
${environmental_base.lib_deps}
${environmental_extra.lib_deps}
${radiolib_base.lib_deps}
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
lewisxhe/XPowersLib@0.3.1
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
rweather/Crypto@0.4.0
build_src_filter =
${esp32_base.build_src_filter} -<mesh/http>
monitor_speed = 460800
monitor_filters = esp32_h2_exception_decoder
lib_ignore =
NonBlockingRTTTL
NimBLE-Arduino
libpax

View File

@@ -0,0 +1,11 @@
[env:waveshare-esp32h2]
extends = esp32h2_base
board = esp32-h2-devkitm-1
board_build.f_flash = 16000000L
board_level = pr
build_flags =
${esp32h2_base.build_flags}
-I variants/esp32h2/waveshare-esp32-h2
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MODE=1
-DHAS_WIFI=0

View File

@@ -0,0 +1,8 @@
#define HAS_SCREEN 0
#define HAS_WIFI 0
#define LORA_SCK 4
#define LORA_MISO 3
#define LORA_MOSI 2
#define LORA_CS 1

View File

@@ -45,7 +45,6 @@ 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}