Merge branch 'master' into nomad-gemini

This commit is contained in:
Thomas Göttgens 2025-01-18 14:11:19 +01:00 committed by GitHub
commit b5737d089b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
161 changed files with 5376 additions and 1436 deletions

View File

@ -0,0 +1,52 @@
# This container is used to build Meshtastic with the libraries required by the fuzzer.
# ClusterFuzzLite starts the container, runs the build.sh script, and then exits.
# As this is not a long running service, health-checks are not required. ClusterFuzzLite
# also only works if the user remains unchanged from the base image (it expects to run
# as root).
# trunk-ignore-all(trivy/DS026): No healthcheck is needed for this builder container
# trunk-ignore-all(checkov/CKV_DOCKER_2): No healthcheck is needed for this builder container
# trunk-ignore-all(checkov/CKV_DOCKER_3): We must run as root for this container
# trunk-ignore-all(trivy/DS002): We must run as root for this container
# trunk-ignore-all(checkov/CKV_DOCKER_8): We must run as root for this container
# trunk-ignore-all(hadolint/DL3002): We must run as root for this container
FROM gcr.io/oss-fuzz-base/base-builder:v1
ENV PIP_ROOT_USER_ACTION=ignore
# trunk-ignore(hadolint/DL3008): apt packages are not pinned.
# trunk-ignore(terrascan/AC_DOCKER_0002): apt packages are not pinned.
RUN apt-get update && apt-get install --no-install-recommends -y \
cmake git zip libgpiod-dev libbluetooth-dev libi2c-dev \
libunistring-dev libmicrohttpd-dev libgnutls28-dev libgcrypt20-dev \
libusb-1.0-0-dev libssl-dev pkg-config && \
apt-get clean && rm -rf /var/lib/apt/lists/* && \
pip install --no-cache-dir -U \
platformio==6.1.16 \
grpcio-tools==1.68.1 \
meshtastic==2.5.9
# Ugly hack to avoid clang detecting a conflict between the math "log" function and the "log" function in framework-portduino/cores/portduino/logging.h
RUN sed -i -e 's/__MATHCALL_VEC (log,, (_Mdouble_ __x));//' /usr/include/x86_64-linux-gnu/bits/mathcalls.h
# A few dependencies are too old on the base-builder image. More recent versions are built from source.
WORKDIR $SRC
RUN git config --global advice.detachedHead false && \
git clone --depth 1 --branch 0.8.0 https://github.com/jbeder/yaml-cpp.git && \
git clone --depth 1 --branch v2.3.3 https://github.com/babelouest/orcania.git && \
git clone --depth 1 --branch v1.4.20 https://github.com/babelouest/yder.git && \
git clone --depth 1 --branch v2.7.15 https://github.com/babelouest/ulfius.git
COPY ./.clusterfuzzlite/build.sh $SRC/
WORKDIR $SRC/firmware
COPY . $SRC/firmware/
# https://docs.platformio.org/en/latest/envvars.html
ENV PLATFORMIO_CORE_DIR=$SRC/pio/core \
PLATFORMIO_LIBDEPS_DIR=$SRC/pio/libdeps \
PLATFORMIO_PACKAGES_DIR=$SRC/pio/packages \
PLATFORMIO_SETTING_ENABLE_CACHE=No \
PIO_ENV=buildroot
RUN platformio pkg install --environment $PIO_ENV

View File

@ -0,0 +1,59 @@
# ClusterFuzzLite for Meshtastic
This directory contains the fuzzer implementation for Meshtastic using the ClusterFuzzLite framework.
See the [ClusterFuzzLite documentation](https://google.github.io/clusterfuzzlite/) for more details.
## Running locally
ClusterFuzzLite uses the OSS-Fuzz toolchain. To build the fuzzer manually, first grab a copy of OSS-Fuzz.
```shell
git clone https://github.com/google/oss-fuzz.git
cd oss-fuzz
```
To build the fuzzer, run:
```shell
python3 infra/helper.py build_image --external $PATH_TO_MESHTASTIC_FIRMWARE_DIRECTORY
python3 infra/helper.py build_fuzzers --external $PATH_TO_MESHTASTIC_FIRMWARE_DIRECTORY --sanitizer address
```
To run the fuzzer, run:
```shell
python3 infra/helper.py run_fuzzer --external --corpus-dir=<path-to-temp-corpus-dir> $PATH_TO_MESHTASTIC_FIRMWARE_DIRECTORY router_fuzzer
```
More background on these commands can be found in the
[ClusterFuzzLite documentation](https://google.github.io/clusterfuzzlite/build-integration/#testing-locally).
## router_fuzzer.cpp
This fuzzer submits MeshPacket protos to the `Router::enqueueReceivedMessage` method. It takes the binary
data from the fuzzer and decodes that data to a MeshPacket using nanopb. A few fields in
the MeshPacket are modified by the fuzzer.
- If the `to` field is 0, it will be replaced with the NodeID of the running node.
- If the `from` field is 0, it will be replaced with the NodeID of the running node.
- If the `id` field is 0, it will be replaced with an incrementing counter value.
- If the `pki_encrypted` field is true, the `public_key` field will be populated with the first admin key.
The `router_fuzzer_seed_corpus.py` file contains a list of MeshPackets. It is run from inside build.sh and
writes the binary MeshPacket protos to files. These files are use used by the fuzzer as its initial seed data,
helping the fuzzer to start off with a few known inputs.
### Interpreting a fuzzer crash
If the fuzzer crashes, it'll write the input bytes used for the test case to a file and notify about the
location of that file. The contents of the file are a binary serialized MeshPacket protobuf. The following
snippet of Python code can be used to parse the file into a human readable form.
```python
from meshtastic.protobuf import mesh_pb2
mesh_pb2.MeshPacket.FromString(open("crash-XXXX-file", "rb").read())
```
Consider adding any such crash results to the `router_fuzzer_seed_corpus.py` file to ensure there a isn't
a future regression for that crash test case.

71
.clusterfuzzlite/build.sh Normal file
View File

@ -0,0 +1,71 @@
#!/bin/bash -eu
# Build Meshtastic and a few needed dependencies using clang++
# and the OSS-Fuzz required build flags.
env
cd "$SRC"
NPROC=$(nproc || echo 1)
LDFLAGS=-lpthread cmake -S "$SRC/yaml-cpp" -B "$WORK/yaml-cpp/$SANITIZER" \
-DBUILD_SHARED_LIBS=OFF
cmake --build "$WORK/yaml-cpp/$SANITIZER" -j "$NPROC"
cmake --install "$WORK/yaml-cpp/$SANITIZER" --prefix /usr
cmake -S "$SRC/orcania" -B "$WORK/orcania/$SANITIZER" \
-DBUILD_STATIC=ON
cmake --build "$WORK/orcania/$SANITIZER" -j "$NPROC"
cmake --install "$WORK/orcania/$SANITIZER" --prefix /usr
cmake -S "$SRC/yder" -B "$WORK/yder/$SANITIZER" \
-DBUILD_STATIC=ON -DWITH_JOURNALD=OFF
cmake --build "$WORK/yder/$SANITIZER" -j "$NPROC"
cmake --install "$WORK/yder/$SANITIZER" --prefix /usr
cmake -S "$SRC/ulfius" -B "$WORK/ulfius/$SANITIZER" \
-DBUILD_STATIC=ON -DWITH_JANSSON=OFF -DWITH_CURL=OFF -DWITH_WEBSOCKET=OFF
cmake --build "$WORK/ulfius/$SANITIZER" -j "$NPROC"
cmake --install "$WORK/ulfius/$SANITIZER" --prefix /usr
cd "$SRC/firmware"
PLATFORMIO_EXTRA_SCRIPTS=$(echo -e "pre:.clusterfuzzlite/platformio-clusterfuzzlite-pre.py\npost:.clusterfuzzlite/platformio-clusterfuzzlite-post.py")
STATIC_LIBS=$(pkg-config --libs --static libulfius openssl libgpiod yaml-cpp bluez --silence-errors)
export PLATFORMIO_EXTRA_SCRIPTS
export STATIC_LIBS
export PLATFORMIO_WORKSPACE_DIR="$WORK/pio/$SANITIZER"
export TARGET_CC=$CC
export TARGET_CXX=$CXX
export TARGET_LD=$CXX
export TARGET_AR=llvm-ar
export TARGET_AS=llvm-as
export TARGET_OBJCOPY=llvm-objcopy
export TARGET_RANLIB=llvm-ranlib
mkdir -p "$OUT/lib"
cp .clusterfuzzlite/*_fuzzer.options "$OUT/"
for f in .clusterfuzzlite/*_fuzzer.cpp; do
fuzzer=$(basename "$f" .cpp)
cp -f "$f" src/fuzzer.cpp
pio run -vvv --environment "$PIO_ENV"
program="$PLATFORMIO_WORKSPACE_DIR/build/$PIO_ENV/program"
cp "$program" "$OUT/$fuzzer"
# Copy shared libraries used by the fuzzer.
read -d '' -ra shared_libs < <(ldd "$program" | sed -n 's/[^=]\+=> \([^ ]\+\).*/\1/p') || true
cp -f "${shared_libs[@]}" "$OUT/lib/"
# Build the initial fuzzer seed corpus.
corpus_name="${fuzzer}_seed_corpus"
corpus_generator="$PWD/.clusterfuzzlite/${corpus_name}.py"
if [[ -f $corpus_generator ]]; then
mkdir "$corpus_name"
pushd "$corpus_name"
python3 "$corpus_generator"
popd
zip -D "$OUT/${corpus_name}.zip" "$corpus_name"/*
fi
done

View File

@ -0,0 +1,35 @@
"""PlatformIO build script (post: runs after other Meshtastic scripts)."""
import os
import shlex
from SCons.Script import DefaultEnvironment
env = DefaultEnvironment()
# Remove any static libraries from the LIBS environment. Static libraries are
# handled in platformio-clusterfuzzlite-pre.py.
static_libs = set(lib[2:] for lib in shlex.split(os.getenv("STATIC_LIBS")))
env.Replace(
LIBS=[
lib for lib in env["LIBS"] if not (isinstance(lib, str) and lib in static_libs)
],
)
# FrameworkArduino/portduino/main.cpp contains the "main" function the binary.
# The fuzzing framework also provides a "main" function and needs to be run
# before Meshtastic is started. We rename the "main" function for Meshtastic to
# "portduino_main" here so that it can be called inside the fuzzer.
env.AddPostAction(
"$BUILD_DIR/FrameworkArduino/portduino/main.cpp.o",
env.VerboseAction(
" ".join(
[
"$OBJCOPY",
"--redefine-sym=main=portduino_main",
"$BUILD_DIR/FrameworkArduino/portduino/main.cpp.o",
]
),
"Renaming main symbol to portduino_main",
),
)

View File

@ -0,0 +1,52 @@
"""PlatformIO build script (pre: runs before other Meshtastic scripts).
ClusterFuzzLite executes in a different container from the build. During the build,
attempt to link statically to as many dependencies as possible. For dependencies that
do not have static libraries, the shared library files are copied to the output
directory by the build.sh script.
"""
import glob
import os
import shlex
from SCons.Script import DefaultEnvironment, Literal
env = DefaultEnvironment()
cxxflags = shlex.split(os.getenv("CXXFLAGS"))
sanitizer_flags = shlex.split(os.getenv("SANITIZER_FLAGS"))
lib_fuzzing_engine = shlex.split(os.getenv("LIB_FUZZING_ENGINE"))
statics = glob.glob("/usr/lib/lib*.a") + glob.glob("/usr/lib/*/lib*.a")
no_static = set(("-ldl",))
def replaceStatic(lib):
"""Replace -l<libname> with the static .a file for the library."""
if not lib.startswith("-l") or lib in no_static:
return lib
static_name = f"/lib{lib[2:]}.a"
static = [s for s in statics if s.endswith(static_name)]
if len(static) == 1:
return static[0]
return lib
# Setup the environment for building with Clang and the OSS-Fuzz required build flags.
env.Append(
CFLAGS=os.getenv("CFLAGS"),
CXXFLAGS=cxxflags,
LIBSOURCE_DIRS=["/usr/lib/x86_64-linux-gnu"],
LINKFLAGS=cxxflags
+ sanitizer_flags
+ lib_fuzzing_engine
+ ["-stdlib=libc++", "-std=c++17"],
_LIBFLAGS=[replaceStatic(s) for s in shlex.split(os.getenv("STATIC_LIBS"))]
+ [
"/usr/lib/x86_64-linux-gnu/libunistring.a", # Needs to be at the end.
# Find the shared libraries in a subdirectory named lib
# within the same directory as the binary.
Literal("-Wl,-rpath,$ORIGIN/lib"),
"-Wl,-z,origin",
],
)

View File

@ -0,0 +1 @@
language: c++

View File

@ -0,0 +1,206 @@
// Fuzzer implementation that sends MeshPackets to Router::enqueueReceivedMessage.
#include <condition_variable>
#include <cstdlib>
#include <mutex>
#include <pb_decode.h>
#include <stdexcept>
#include <string>
#include <thread>
#include "PortduinoGPIO.h"
#include "PortduinoGlue.h"
#include "PowerFSM.h"
#include "mesh/MeshTypes.h"
#include "mesh/NodeDB.h"
#include "mesh/Router.h"
#include "mesh/TypeConversions.h"
#include "mesh/mesh-pb-constants.h"
namespace
{
constexpr uint32_t nodeId = 0x12345678;
// Set to true when lateInitVariant finishes. Used to ensure lateInitVariant was called during startup.
bool hasBeenConfigured = false;
// These are used to block the Arduino loop() function until a fuzzer input is ready. This is
// an optimization that prevents a sleep from happening before the loop is run. The Arduino loop
// function calls loopCanSleep() before sleeping. loopCanSleep is implemented here in the fuzzer
// and blocks until runLoopOnce() is called to signal for the loop to run.
bool fuzzerRunning = false; // Set to true once LLVMFuzzerTestOneInput has started running.
bool loopCanRun = true; // The main Arduino loop() can run when this is true.
bool loopIsWaiting = false; // The main Arduino loop() is waiting to be signaled to run.
bool loopShouldExit = false; // Indicates that the main Arduino thread should exit by throwing ShouldExitException.
std::mutex loopLock;
std::condition_variable loopCV;
std::thread meshtasticThread;
// This exception is thrown when the portuino main thread should exit.
class ShouldExitException : public std::runtime_error
{
public:
using std::runtime_error::runtime_error;
};
// Start the loop for one test case and wait till the loop has completed. This ensures fuzz
// test cases do not overlap with one another. This helps the fuzzer attribute a crash to the
// single, currently running, test case.
void runLoopOnce()
{
realHardware = true; // Avoids delay(100) within portduino/main.cpp
std::unique_lock<std::mutex> lck(loopLock);
fuzzerRunning = true;
loopCanRun = true;
loopCV.notify_one();
loopCV.wait(lck, [] { return !loopCanRun && loopIsWaiting; });
}
} // namespace
// Called in the main Arduino loop function to determine if the loop can delay/sleep before running again.
// We use this as a way to block the loop from sleeping and to start the loop function immediately when a
// fuzzer input is ready.
bool loopCanSleep()
{
std::unique_lock<std::mutex> lck(loopLock);
loopIsWaiting = true;
loopCV.notify_one();
loopCV.wait(lck, [] { return loopCanRun || loopShouldExit; });
loopIsWaiting = false;
if (loopShouldExit)
throw ShouldExitException("exit");
if (!fuzzerRunning)
return true; // The loop can sleep before the fuzzer starts.
loopCanRun = false; // Only run the loop once before waiting again.
return false;
}
// Called just prior to starting Meshtastic. Allows for setting config values before startup.
void lateInitVariant()
{
settingsMap[logoutputlevel] = level_error;
channelFile.channels[0] = meshtastic_Channel{
.has_settings = true,
.settings =
meshtastic_ChannelSettings{
.psk = {.size = 1, .bytes = {/*defaultpskIndex=*/1}},
.name = "LongFast",
.uplink_enabled = true,
.has_module_settings = true,
.module_settings = {.position_precision = 16},
},
.role = meshtastic_Channel_Role_PRIMARY,
};
config.security.admin_key[0] = {
.size = 32,
.bytes = {0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, 0x0c, 0x0d, 0xec, 0x85, 0x5a,
0x4c, 0xf6, 0x1a, 0x96, 0x04, 0x1a, 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c},
};
config.security.admin_key_count = 1;
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_US;
moduleConfig.has_mqtt = true;
moduleConfig.mqtt = meshtastic_ModuleConfig_MQTTConfig{
.enabled = true,
.proxy_to_client_enabled = true,
};
moduleConfig.has_store_forward = true;
moduleConfig.store_forward = meshtastic_ModuleConfig_StoreForwardConfig{
.enabled = true,
.history_return_max = 4,
.history_return_window = 600,
.is_server = true,
};
meshtastic_Position fixedGPS = meshtastic_Position{
.has_latitude_i = true,
.latitude_i = static_cast<uint32_t>(1 * 1e7),
.has_longitude_i = true,
.longitude_i = static_cast<uint32_t>(3 * 1e7),
.has_altitude = true,
.altitude = 64,
.location_source = meshtastic_Position_LocSource_LOC_MANUAL,
};
nodeDB->setLocalPosition(fixedGPS);
config.has_position = true;
config.position.fixed_position = true;
meshtastic_NodeInfoLite *info = nodeDB->getMeshNode(nodeDB->getNodeNum());
info->has_position = true;
info->position = TypeConversions::ConvertToPositionLite(fixedGPS);
hasBeenConfigured = true;
}
extern "C" {
int portduino_main(int argc, char **argv); // Renamed "main" function from Meshtastic binary.
// Start Meshtastic in a thread and wait till it has reached the ON state.
int LLVMFuzzerInitialize(int *argc, char ***argv)
{
settingsMap[maxtophone] = 5;
meshtasticThread = std::thread([program = *argv[0]]() {
char nodeIdStr[12];
strcpy(nodeIdStr, std::to_string(nodeId).c_str());
int argc = 7;
char *argv[] = {program, "-d", "/tmp/meshtastic", "-h", nodeIdStr, "-p", "0", nullptr};
try {
portduino_main(argc, argv);
} catch (const ShouldExitException &) {
}
});
std::atexit([] {
{
const std::lock_guard<std::mutex> lck(loopLock);
loopShouldExit = true;
loopCV.notify_one();
}
meshtasticThread.join();
});
// Wait for startup.
for (int i = 1; i < 20; ++i) {
if (powerFSM.getState() == &stateON) {
assert(hasBeenConfigured);
assert(router);
assert(nodeDB);
return 0;
}
std::this_thread::sleep_for(std::chrono::seconds(1));
}
return 1;
}
// This is the main entrypoint for the fuzzer (the fuzz target). The fuzzer will provide an array of bytes to be
// interpreted by this method. To keep things simple, the bytes are interpreted as a binary serialized MeshPacket
// proto. Any crashes discovered by the fuzzer will be written to a file. Unserialize that file to print the MeshPacket
// that caused the failure.
//
// This guide provides best practices for writing a fuzzer target.
// https://github.com/google/fuzzing/blob/master/docs/good-fuzz-target.md
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t length)
{
meshtastic_MeshPacket p = meshtastic_MeshPacket_init_default;
pb_istream_t stream = pb_istream_from_buffer(data, length);
// Ignore any inputs that fail to decode or have fields set that are not transmitted over LoRa.
if (!pb_decode(&stream, &meshtastic_MeshPacket_msg, &p) || p.rx_time || p.rx_snr || p.priority || p.rx_rssi || p.delayed ||
p.public_key.size || p.next_hop || p.relay_node || p.tx_after)
return -1; // Reject: The input will not be added to the corpus.
if (p.which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
meshtastic_Data d;
stream = pb_istream_from_buffer(p.decoded.payload.bytes, p.decoded.payload.size);
if (!pb_decode(&stream, &meshtastic_Data_msg, &d))
return -1; // Reject: The input will not be added to the corpus.
}
// Provide default values for a few fields so the fuzzer doesn't need to guess them.
if (p.from == 0)
p.from = nodeDB->getNodeNum();
if (p.to == 0)
p.to = nodeDB->getNodeNum();
static uint32_t packetId = 0;
if (p.id == 0)
p.id == ++packetId;
if (p.pki_encrypted && config.security.admin_key_count)
memcpy(&p.public_key, &config.security.admin_key[0], sizeof(p.public_key));
router->enqueueReceivedMessage(packetPool.allocCopy(p));
runLoopOnce();
return 0; // Accept: The input may be added to the corpus.
}
}

View File

@ -0,0 +1,2 @@
[libfuzzer]
max_len=256

View File

@ -0,0 +1,168 @@
"""Generate an initial set of MeshPackets.
The fuzzer uses these MeshPackets as an initial seed of test candidates.
It's also good to add any previously discovered crash test cases to this list
to avoid future regressions.
If left unset, the following values will be automatically set by the fuzzer.
- to: automatically set to the running node's NodeID
- from: automatically set to the running node's NodeID
- id: automatically set to the value of an incrementing counter
Additionally, if `pki_encrypted` is populated in the packet, the first admin key
will be copied into the `public_key` field.
"""
import base64
from meshtastic import BROADCAST_NUM
from meshtastic.protobuf import (
admin_pb2,
atak_pb2,
mesh_pb2,
portnums_pb2,
telemetry_pb2,
)
def From(node: int = 9):
"""Return a dict suitable for **kwargs for populating the 'from' field.
'from' is a reserved keyword in Python. It can't be used directly as an
argument to the MeshPacket constructor. Rather **From() can be used as
the final argument to provide the from node as a **kwarg.
Defaults to 9 if no value is provided.
"""
return {"from": node}
packets = (
(
"position",
mesh_pb2.MeshPacket(
decoded=mesh_pb2.Data(
portnum=portnums_pb2.PortNum.POSITION_APP,
payload=mesh_pb2.Position(
latitude_i=int(1 * 1e7),
longitude_i=int(2 * 1e7),
altitude=5,
precision_bits=32,
).SerializeToString(),
),
to=BROADCAST_NUM,
**From(),
),
),
(
"telemetry",
mesh_pb2.MeshPacket(
decoded=mesh_pb2.Data(
portnum=portnums_pb2.PortNum.TELEMETRY_APP,
payload=telemetry_pb2.Telemetry(
time=1736192207,
device_metrics=telemetry_pb2.DeviceMetrics(
battery_level=101,
channel_utilization=8,
air_util_tx=2,
uptime_seconds=42,
),
).SerializeToString(),
),
to=BROADCAST_NUM,
**From(),
),
),
(
"text",
mesh_pb2.MeshPacket(
decoded=mesh_pb2.Data(
portnum=portnums_pb2.PortNum.TEXT_MESSAGE_APP,
payload=b"Hello world",
),
to=BROADCAST_NUM,
**From(),
),
),
(
"user",
mesh_pb2.MeshPacket(
decoded=mesh_pb2.Data(
portnum=portnums_pb2.PortNum.NODEINFO_APP,
payload=mesh_pb2.User(
id="!00000009",
long_name="Node 9",
short_name="N9",
macaddr=b"\x00\x00\x00\x00\x00\x09",
hw_model=mesh_pb2.HardwareModel.RAK4631,
public_key=base64.b64decode(
"L0ih/6F41itofdE8mYyHk1SdfOJ/QRM1KQ+pO4vEEjQ="
),
).SerializeToString(),
),
**From(),
),
),
(
"traceroute",
mesh_pb2.MeshPacket(
decoded=mesh_pb2.Data(
portnum=portnums_pb2.PortNum.TRACEROUTE_APP,
payload=mesh_pb2.RouteDiscovery(
route=[10],
).SerializeToString(),
),
**From(),
),
),
(
"routing",
mesh_pb2.MeshPacket(
decoded=mesh_pb2.Data(
portnum=portnums_pb2.PortNum.ROUTING_APP,
payload=mesh_pb2.Routing(
error_reason=mesh_pb2.Routing.NO_RESPONSE,
).SerializeToString(),
),
**From(),
),
),
(
"admin",
mesh_pb2.MeshPacket(
decoded=mesh_pb2.Data(
portnum=portnums_pb2.PortNum.ADMIN_APP,
payload=admin_pb2.AdminMessage(
get_owner_request=True,
).SerializeToString(),
),
pki_encrypted=True,
**From(),
),
),
(
"atak",
mesh_pb2.MeshPacket(
decoded=mesh_pb2.Data(
portnum=portnums_pb2.PortNum.ATAK_PLUGIN,
payload=atak_pb2.TAKPacket(
is_compressed=True,
# Note, the strings are not valid for a compressed message, but will
# give the fuzzer a starting point.
contact=atak_pb2.Contact(
callsign="callsign", device_callsign="device_callsign"
),
chat=atak_pb2.GeoChat(
message="message", to="to", to_callsign="to_callsign"
),
).SerializeToString(),
),
**From(),
),
),
)
for name, packet in packets:
with open(f"{name}.MeshPacket", "wb") as f:
f.write(packet.SerializeToString())

1
.dockerignore Symbolic link
View File

@ -0,0 +1 @@
.gitignore

View File

@ -68,6 +68,12 @@ runs:
sed -i '/DDEBUG_HEAP/d' ${INI_FILE}
done
- name: PlatformIO ${{ inputs.arch }} download cache
uses: actions/cache@v4
with:
path: ~/.platformio/.cache
key: pio-cache-${{ inputs.arch }}-${{ hashFiles('.github/actions/**', '**.ini') }}
- name: Build ${{ inputs.board }}
shell: bash
run: ${{ inputs.build-script-path }} ${{ inputs.board }}
@ -83,13 +89,13 @@ runs:
- name: Get release version string
shell: bash
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
with:
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
overwrite: true
path: |
${{ inputs.artifact-paths }}

View File

@ -20,19 +20,16 @@ runs:
shell: bash
run: |
sudo apt-get -y update --fix-missing
sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev
sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev lsb-release
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: 3.x
# - name: Cache python libs
# uses: actions/cache@v4
# id: cache-pip # needed in if test
# with:
# path: ~/.cache/pip
# key: ${{ runner.os }}-pip
cache: pip
cache-dependency-path: |
.github/actions/**
**.ini
- name: Upgrade python tools
shell: bash

14
.github/actions/setup-native/action.yml vendored Normal file
View File

@ -0,0 +1,14 @@
name: Setup native build
description: Install libraries needed for building the Native/Portduino build
runs:
using: composite
steps:
- name: Setup base
id: base
uses: ./.github/actions/setup-base
- name: Install libs needed for native build
shell: bash
run: |
sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev

72
.github/workflows/build_debian_src.yml vendored Normal file
View File

@ -0,0 +1,72 @@
name: Build Debian Source Package
on:
workflow_call:
secrets:
PPA_GPG_PRIVATE_KEY:
required: true
inputs:
series:
description: Ubuntu/Debian series to target
required: true
type: string
build_location:
description: Location where build will execute
required: true
type: string
permissions:
contents: write
packages: write
jobs:
build-debian-src:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
path: meshtasticd
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Install deps
shell: bash
working-directory: meshtasticd
run: |
sudo apt-get update -y --fix-missing
sudo apt-get install -y software-properties-common build-essential devscripts equivs
sudo add-apt-repository ppa:meshtastic/build-tools -y
sudo apt-get update -y --fix-missing
sudo mk-build-deps --install --remove --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@v6
with:
gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }}
id: gpg
- name: Get release version string
working-directory: meshtasticd
run: |
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
env:
BUILD_LOCATION: ${{ inputs.build_location }}
id: version
- name: Fetch libdeps, package debian source
working-directory: meshtasticd
run: debian/ci_pack_sdeb.sh
env:
SERIES: ${{ inputs.series }}
GPG_KEY_ID: ${{ steps.gpg.outputs.keyid }}
PKG_VERSION: ${{ steps.version.outputs.deb }}
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
overwrite: true
path: |
meshtasticd_${{ steps.version.outputs.deb }}*

View File

@ -18,7 +18,7 @@ jobs:
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Docker login
@ -39,7 +39,7 @@ jobs:
context: .
file: ./Dockerfile
push: true
tags: meshtastic/meshtasticd:${{ steps.version.outputs.version }}
tags: meshtastic/meshtasticd:${{ steps.version.outputs.long }}
- name: Docker build and push
if: ${{ github.ref == 'refs/heads/master' && github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}

View File

@ -10,12 +10,6 @@ jobs:
build-native:
runs-on: ubuntu-latest
steps:
- name: Install libs needed for native build
shell: bash
run: |
sudo apt-get update --fix-missing
sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev
- name: Checkout code
uses: actions/checkout@v4
with:
@ -23,29 +17,21 @@ jobs:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Upgrade python tools
shell: bash
run: |
python -m pip install --upgrade pip
pip install -U platformio adafruit-nrfutil
pip install -U meshtastic --pre
- name: Upgrade platformio
shell: bash
run: |
pio upgrade
- name: Setup native build
id: base
uses: ./.github/actions/setup-native
- name: Build Native
run: bin/build-native.sh
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
with:
name: firmware-native-${{ steps.version.outputs.version }}.zip
name: firmware-native-${{ steps.version.outputs.long }}.zip
overwrite: true
path: |
release/meshtasticd_linux_x86_64

View File

@ -39,13 +39,13 @@ jobs:
run: bin/build-native.sh
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
with:
name: firmware-raspbian-${{ steps.version.outputs.version }}.zip
name: firmware-raspbian-${{ steps.version.outputs.long }}.zip
overwrite: true
path: |
release/meshtasticd_linux_aarch64

View File

@ -39,13 +39,13 @@ jobs:
run: bin/build-native.sh
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
with:
name: firmware-raspbian-armv7l-${{ steps.version.outputs.version }}.zip
name: firmware-raspbian-armv7l-${{ steps.version.outputs.long }}.zip
overwrite: true
path: |
release/meshtasticd_linux_armv7l

45
.github/workflows/daily_packaging.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: Daily Packaging
on:
schedule:
- cron: 0 9 * * *
workflow_dispatch:
push:
branches:
- master
paths:
- debian/**
- "*.rpkg"
- .github/workflows/nightly_packaging.yml
- .github/workflows/build_debian_src.yml
- .github/workflows/package_ppa.yml
- .github/workflows/package_obs.yml
- .github/workflows/hook_copr.yml
permissions:
contents: write
packages: write
jobs:
package-ppa:
strategy:
fail-fast: false
matrix:
series: [plucky, oracular, noble, jammy]
uses: ./.github/workflows/package_ppa.yml
with:
ppa_repo: ppa:meshtastic/daily
series: ${{ matrix.series }}
secrets: inherit
package-obs:
uses: ./.github/workflows/package_obs.yml
with:
obs_project: home:meshtastic:daily
series: unstable
secrets: inherit
hook-copr:
uses: ./.github/workflows/hook_copr.yml
with:
copr_project: daily
secrets: inherit

38
.github/workflows/hook_copr.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: Trigger COPR build
on:
workflow_call:
secrets:
COPR_API_CONFIG:
inputs:
copr_project:
description: COPR project to target
required: true
type: string
permissions:
contents: write
packages: write
jobs:
build-copr-hook:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
ref: ${{ github.ref }}
repository: ${{ github.repository }}
- name: Trigger COPR build
uses: vidplace7/copr-build@main
id: copr_build
env:
COPR_API_TOKEN_CONFIG: ${{ secrets.COPR_API_CONFIG }}
with:
owner: "@meshtastic"
package-name: meshtasticd
project-name: ${{ inputs.copr_project }}
git-remote: "${{ github.server_url }}/${{ github.repository }}.git"
committish: ${{ github.sha }}

View File

@ -137,6 +137,16 @@ jobs:
package-native:
uses: ./.github/workflows/package_amd64.yml
build-debian-src:
uses: ./.github/workflows/build_debian_src.yml
with:
series: UNRELEASED
build_location: local
secrets: inherit
test-native:
uses: ./.github/workflows/test_native.yml
build-docker:
if: ${{ github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/build_docker.yml
@ -189,7 +199,7 @@ jobs:
run: ls -R
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Move files up
@ -198,7 +208,7 @@ jobs:
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v4
with:
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
overwrite: true
path: |
./firmware-*.bin
@ -215,7 +225,7 @@ jobs:
- uses: actions/download-artifact@v4
with:
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
merge-multiple: true
path: ./output
@ -229,12 +239,12 @@ jobs:
chmod +x ./output/device-update.sh
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip ./output
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip
uses: actions/upload-artifact@v4
with:
name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
overwrite: true
path: ./*.elf
retention-days: 30
@ -242,8 +252,8 @@ jobs:
- uses: scruplelesswizard/comment-artifact@main
if: ${{ github.event_name == 'pull_request' }}
with:
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}
description: "Download firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip. This artifact will be available for 90 days from creation"
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
description: "Download firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
github-token: ${{ secrets.GITHUB_TOKEN }}
release-artifacts:
@ -257,6 +267,7 @@ jobs:
package-raspbian,
package-raspbian-armv7l,
package-native,
build-debian-src,
]
steps:
- name: Checkout
@ -268,73 +279,70 @@ jobs:
python-version: 3.x
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
id: version
env:
BUILD_LOCATION: local
- name: Create release
uses: actions/create-release@v1
uses: softprops/action-gh-release@v2
id: create_release
with:
draft: true
prerelease: true
release_name: Meshtastic Firmware ${{ steps.version.outputs.version }} Alpha
tag_name: v${{ steps.version.outputs.version }}
name: Meshtastic Firmware ${{ steps.version.outputs.long }} Alpha
tag_name: v${{ steps.version.outputs.long }}
body: |
Autogenerated by github action, developer should edit as required before publishing...
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Download deb files
uses: actions/download-artifact@v4
with:
pattern: meshtasticd_${{ steps.version.outputs.version }}_*.deb
pattern: meshtasticd_${{ steps.version.outputs.long }}_*.deb
merge-multiple: true
path: ./output
- name: Download source deb
uses: actions/download-artifact@v4
with:
pattern: firmware-debian-${{ steps.version.outputs.deb }}~UNRELEASED-src
merge-multiple: true
path: ./output/debian-src
- name: Zip source deb
working-directory: output
run: zip -j -9 -r ./meshtasticd-${{ steps.version.outputs.deb }}-src.zip ./debian-src
# For diagnostics
- name: Display structure of downloaded files
run: ls -lR
- name: Add raspbian aarch64 .deb
uses: actions/upload-release-asset@v1
- name: Add deb files to release
run: |
gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_arm64.deb
gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_armhf.deb
gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd_${{ steps.version.outputs.long }}_amd64.deb
gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd-${{ steps.version.outputs.deb }}-src.zip
env:
GITHUB_TOKEN: ${{ github.token }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./output/meshtasticd_${{ steps.version.outputs.version }}_arm64.deb
asset_name: meshtasticd_${{ steps.version.outputs.version }}_arm64.deb
asset_content_type: application/vnd.debian.binary-package
- name: Add raspbian armv7l .deb
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ github.token }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./output/meshtasticd_${{ steps.version.outputs.version }}_armhf.deb
asset_name: meshtasticd_${{ steps.version.outputs.version }}_armhf.deb
asset_content_type: application/vnd.debian.binary-package
- name: Add raspbian amd64 .deb
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ github.token }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./output/meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
asset_name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
asset_content_type: application/vnd.debian.binary-package
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Bump version.properties
run: >-
bin/bump_version.py
- name: Update debian changelog
run: >-
debian/ci_changelog.sh
- name: Create version.properties pull request
uses: peter-evans/create-pull-request@v7
with:
title: Bump version.properties
add-paths: |
version.properties
debian/changelog
release-firmware:
strategy:
@ -354,12 +362,12 @@ jobs:
python-version: 3.x
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- uses: actions/download-artifact@v4
with:
pattern: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}
pattern: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
merge-multiple: true
path: ./output
@ -372,37 +380,24 @@ jobs:
chmod +x ./output/device-update.sh
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip ./output
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v4
with:
name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
merge-multiple: true
path: ./elfs
- name: Zip firmware
run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip ./elfs
- name: Zip debug elfs
run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip ./elfs
# For diagnostics
- name: Display structure of downloaded files
run: ls -lR
- name: Add bins to release
uses: actions/upload-release-asset@v1
- name: Add bins and debug elfs to release
run: |
gh release upload v${{ steps.version.outputs.long }} ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
gh release upload v${{ steps.version.outputs.long }} ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
env:
GITHUB_TOKEN: ${{ github.token }}
with:
upload_url: ${{needs.release-artifacts.outputs.upload_url}}
asset_path: ./firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
asset_name: firmware-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
asset_content_type: application/zip
- name: Add debug elfs to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ github.token }}
with:
upload_url: ${{needs.release-artifacts.outputs.upload_url}}
asset_path: ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
asset_name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.version }}.zip
asset_content_type: application/zip
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -32,13 +32,13 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: firmware-native-${{ steps.version.outputs.version }}.zip
name: firmware-native-${{ steps.version.outputs.long }}.zip
merge-multiple: true
- name: Display structure of downloaded files
@ -77,14 +77,14 @@ jobs:
package: meshtasticd
package_root: .debpkg
maintainer: Jonathan Bennett
version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.*
version: ${{ steps.version.outputs.long }} # refs/tags/v*.*.*
arch: amd64
depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0
desc: Native Linux Meshtastic binary.
- uses: actions/upload-artifact@v4
with:
name: meshtasticd_${{ steps.version.outputs.version }}_amd64.deb
name: meshtasticd_${{ steps.version.outputs.long }}_amd64.deb
overwrite: true
path: |
./*.deb

110
.github/workflows/package_obs.yml vendored Normal file
View File

@ -0,0 +1,110 @@
name: Package for OpenSUSE Build Service
on:
workflow_call:
secrets:
OBS_PASSWORD:
required: true
PPA_GPG_PRIVATE_KEY:
required: true
inputs:
obs_project:
description: Meshtastic OBS project to target
required: true
type: string
series:
description: Debian series to target
required: true
type: string
permissions:
contents: write
packages: write
jobs:
build-debian-src:
uses: ./.github/workflows/build_debian_src.yml
secrets: inherit
with:
series: ${{ inputs.series }}
build_location: obs
package-obs:
runs-on: ubuntu-24.04
needs: build-debian-src
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
path: meshtasticd
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Install OpenSUSE Build Service deps
shell: bash
run: |
echo 'deb http://download.opensuse.org/repositories/openSUSE:/Tools/xUbuntu_24.04/ /' | sudo tee /etc/apt/sources.list.d/openSUSE:Tools.list
curl -fsSL https://download.opensuse.org/repositories/openSUSE:Tools/xUbuntu_24.04/Release.key | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/openSUSE_Tools.gpg > /dev/null
sudo apt-get update -y --fix-missing
sudo apt-get install -y osc
- name: Get release version string
working-directory: meshtasticd
run: |
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
env:
BUILD_LOCATION: obs
id: version
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
merge-multiple: true
- name: Display structure of downloaded files
run: ls -lah
- name: Configure osc
env:
OBS_USERNAME: meshtastic
run: |
# Setup OpenSUSE Build Service credentials
mkdir -p ~/.config/osc
echo "[general]" > ~/.config/osc/oscrc
echo "apiurl=https://api.opensuse.org" >> ~/.config/osc/oscrc
echo "[https://api.opensuse.org]" >> ~/.config/osc/oscrc
echo "user=${{ env.OBS_USERNAME }}" >> ~/.config/osc/oscrc
echo "pass=${{ secrets.OBS_PASSWORD }}" >> ~/.config/osc/oscrc
echo "credentials_mgr_class=osc.credentials.PlaintextConfigFileCredentialsManager" >> ~/.config/osc/oscrc
# Create a temporary directory for osc checkout
mkdir -p osc
# Intentionally fail if credentials are invalid
# Update secrets if this returns `401`
- name: Verify OBS authentication
run: osc token
- name: Upload package to OBS
shell: bash
working-directory: osc
env:
OBS_PROJECT: ${{ inputs.obs_project }}
OBS_PACKAGE: meshtasticd
run: |
# Initialize the package in the current directory
osc checkout --output-dir . $OBS_PROJECT $OBS_PACKAGE
# Remove the existing package files
rm -rf *.dsc *.tar.xz
# Copy new package files to the directory
cp $GITHUB_WORKSPACE/*.dsc .
cp $GITHUB_WORKSPACE/*.tar.xz .
# Add/Remove the files
osc addremove
# Commit changes and push to OpenSUSE Build Service
osc commit -m "GitHub Actions: ${{ steps.version.outputs.deb }}~${{ inputs.series }}"

74
.github/workflows/package_ppa.yml vendored Normal file
View File

@ -0,0 +1,74 @@
name: Package for Launchpad PPA
on:
workflow_call:
secrets:
PPA_GPG_PRIVATE_KEY:
required: true
inputs:
ppa_repo:
description: Meshtastic PPA to target
required: true
type: string
series:
description: Ubuntu series to target
required: true
type: string
permissions:
contents: write
packages: write
jobs:
build-debian-src:
uses: ./.github/workflows/build_debian_src.yml
secrets: inherit
with:
series: ${{ inputs.series }}
build_location: ppa
package-ppa:
runs-on: ubuntu-24.04
needs: build-debian-src
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
path: meshtasticd
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Install deps
shell: bash
run: |
sudo apt-get update -y --fix-missing
sudo apt-get install -y dput
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@v6
with:
gpg_private_key: ${{ secrets.PPA_GPG_PRIVATE_KEY }}
id: gpg
- name: Get release version string
working-directory: meshtasticd
run: |
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
env:
BUILD_LOCATION: ppa
id: version
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
merge-multiple: true
- name: Display structure of downloaded files
run: ls -lah
- name: Publish with dput
if: ${{ github.event_name != 'pull_request_target' && github.event_name != 'pull_request' }}
run: |
dput ${{ inputs.ppa_repo }} meshtasticd_${{ steps.version.outputs.deb }}~${{ inputs.series }}_source.changes

View File

@ -32,13 +32,13 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: firmware-raspbian-${{ steps.version.outputs.version }}.zip
name: firmware-raspbian-${{ steps.version.outputs.long }}.zip
merge-multiple: true
- name: Display structure of downloaded files
@ -77,14 +77,14 @@ jobs:
package: meshtasticd
package_root: .debpkg
maintainer: Jonathan Bennett
version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.*
version: ${{ steps.version.outputs.long }} # refs/tags/v*.*.*
arch: arm64
depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0
desc: Native Linux Meshtastic binary.
- uses: actions/upload-artifact@v4
with:
name: meshtasticd_${{ steps.version.outputs.version }}_arm64.deb
name: meshtasticd_${{ steps.version.outputs.long }}_arm64.deb
overwrite: true
path: |
./*.deb

View File

@ -32,13 +32,13 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Download artifacts
uses: actions/download-artifact@v4
with:
name: firmware-raspbian-armv7l-${{ steps.version.outputs.version }}.zip
name: firmware-raspbian-armv7l-${{ steps.version.outputs.long }}.zip
merge-multiple: true
- name: Display structure of downloaded files
@ -77,14 +77,14 @@ jobs:
package: meshtasticd
package_root: .debpkg
maintainer: Jonathan Bennett
version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.*
version: ${{ steps.version.outputs.long }} # refs/tags/v*.*.*
arch: armhf
depends: libyaml-cpp0.7, openssl, libulfius2.7, libi2c0
desc: Native Linux Meshtastic binary.
- uses: actions/upload-artifact@v4
with:
name: meshtasticd_${{ steps.version.outputs.version }}_armhf.deb
name: meshtasticd_${{ steps.version.outputs.long }}_armhf.deb
overwrite: true
path: |
./*.deb

38
.github/workflows/release_channels.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: Trigger release workflows upon Publish
on:
release:
types: [published, released]
permissions:
contents: write
packages: write
jobs:
package-ppa:
strategy:
fail-fast: false
matrix:
series: [plucky, oracular, noble, jammy]
uses: ./.github/workflows/package_ppa.yml
with:
ppa_repo: |-
ppa:meshtastic/${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }}
series: ${{ matrix.series }}
secrets: inherit
package-obs:
uses: ./.github/workflows/package_obs.yml
with:
obs_project: |-
home:meshtastic:${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }}
series: |-
${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }}
secrets: inherit
# hook-copr:
# uses: ./.github/workflows/hook_copr.yml
# with:
# copr_project: |-
# ${{ contains(github.event.release.name, 'Beta') && 'beta' || contains(github.event.release.name, 'Alpha') && 'alpha' }}
# secrets: inherit

169
.github/workflows/test_native.yml vendored Normal file
View File

@ -0,0 +1,169 @@
name: Run Tests on Native platform
on:
workflow_call:
workflow_dispatch:
permissions: {}
env:
LCOV_CAPTURE_FLAGS: --quiet --capture --include "${PWD}/src/*" --exclude '*/src/mesh/generated/*' --directory .pio/build/coverage/src --base-directory "${PWD}"
jobs:
simulator-tests:
name: Native Simulator Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
submodules: recursive
- name: Setup native build
id: base
uses: ./.github/actions/setup-native
- name: Install simulator dependencies
run: pip install -U dotmap
# We now run integration test before other build steps (to quickly see runtime failures)
- name: Build for native/coverage
run: platformio run -e coverage
- name: Capture initial coverage information
shell: bash
run: |
sudo apt-get install -y lcov
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --initial --output-file coverage_base.info
sed -i -e "s#${PWD}#.#" coverage_base.info # Make paths relative.
- name: Integration test
run: |
.pio/build/coverage/program &
PID=$!
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
echo "Simulator started, launching python test..."
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
wait
- name: Capture coverage information
if: always() # run this step even if previous step failed
run: |
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --test-name integration --output-file coverage_integration.info
sed -i -e "s#${PWD}#.#" coverage_integration.info # Make paths relative.
- name: Get release version string
if: always() # run this step even if previous step failed
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Save coverage information
uses: actions/upload-artifact@v4
if: always() # run this step even if previous step failed
with:
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}.zip
overwrite: true
path: ./coverage_*.info
platformio-tests:
name: Native PlatformIO Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
submodules: recursive
- name: Setup native build
id: base
uses: ./.github/actions/setup-native
- name: Get release version string
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
# Disable (comment-out) BUILD_EPOCH. It causes a full rebuild between tests and resets the
# coverage information each time.
- name: Disable BUILD_EPOCH
run: sed -i 's/-DBUILD_EPOCH=$UNIX_TIME/#-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
- name: PlatformIO Tests
run: platformio test -e coverage -v --junit-output-path testreport.xml
- name: Save test results
if: always() # run this step even if previous step failed
uses: actions/upload-artifact@v4
with:
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
overwrite: true
path: ./testreport.xml
- name: Capture coverage information
if: always() # run this step even if previous step failed
run: |
sudo apt-get install -y lcov
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --test-name tests --output-file coverage_tests.info
sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative.
- name: Save coverage information
uses: actions/upload-artifact@v4
if: always() # run this step even if previous step failed
with:
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}.zip
overwrite: true
path: ./coverage_*.info
generate-reports:
name: Generate Test Reports
runs-on: ubuntu-latest
permissions: # Needed for dorny/test-reporter.
contents: read
actions: read
checks: write
needs:
- simulator-tests
- platformio-tests
if: always()
steps:
- uses: actions/checkout@v4
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Get release version string
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Download test artifacts
uses: actions/download-artifact@v4
with:
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
merge-multiple: true
- name: Test Report
uses: dorny/test-reporter@v1.9.1
with:
name: PlatformIO Tests
path: testreport.xml
reporter: java-junit
- name: Download coverage artifacts
uses: actions/download-artifact@v4
with:
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}.zip
path: code-coverage-report
merge-multiple: true
- name: Generate Code Coverage Report
run: |
sudo apt-get install -y lcov
lcov --quiet --add-tracefile code-coverage-report/coverage_base.info --add-tracefile code-coverage-report/coverage_integration.info --add-tracefile code-coverage-report/coverage_tests.info --output-file code-coverage-report/coverage_src.info
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@v4
with:
name: code-coverage-report-${{ steps.version.outputs.long }}.zip
path: code-coverage-report

View File

@ -6,79 +6,8 @@ on:
workflow_dispatch: {}
jobs:
test-simulator:
runs-on: ubuntu-latest
env:
LCOV_CAPTURE_FLAGS: --quiet --capture --include "${PWD}/src/*" --exclude '*/src/mesh/generated/*' --directory .pio/build/coverage/src --base-directory "${PWD}"
steps:
- name: Install libs needed for native build
shell: bash
run: |
sudo apt-get update --fix-missing
sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev libusb-1.0-0-dev libi2c-dev
sudo apt-get install -y lcov
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Upgrade python tools
shell: bash
run: |
python -m pip install --upgrade pip
pip install -U platformio adafruit-nrfutil dotmap
pip install -U meshtastic --pre
- name: Upgrade platformio
shell: bash
run: |
pio upgrade
- name: Build Native
run: bin/build-native.sh
# We now run integration test before other build steps (to quickly see runtime failures)
- name: Build for native/coverage
run: |
platformio run -e coverage
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --initial --output-file coverage_base.info
- name: Integration test
run: |
.pio/build/coverage/program &
PID=$!
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
echo "Simulator started, launching python test..."
python3 -c 'from meshtastic.test import testSimulator; testSimulator()'
wait
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --test-name integration --output-file coverage_integration.info
- name: PlatformIO Tests
run: |
platformio test -e coverage --junit-output-path testreport.xml
lcov ${{ env.LCOV_CAPTURE_FLAGS }} --test-name tests --output-file coverage_tests.info
- name: Test Report
uses: dorny/test-reporter@v1.9.1
if: success() || failure() # run this step even if previous step failed
with:
name: PlatformIO Tests
path: testreport.xml
reporter: java-junit
- name: Generate Code Coverage Report
run: |
lcov --quiet --add-tracefile coverage_base.info --add-tracefile coverage_integration.info --add-tracefile coverage_tests.info --output-file coverage_src.info
mkdir code-coverage-report
genhtml --quiet --legend --prefix "${PWD}" coverage_src.info --output-directory code-coverage-report
mv coverage_*.info code-coverage-report
- name: Save Code Coverage Report
uses: actions/upload-artifact@v4
with:
name: code-coverage-report
path: code-coverage-report
native-tests:
uses: ./.github/workflows/test_native.yml
hardware-tests:
runs-on: test-runner

View File

@ -22,12 +22,16 @@ jobs:
- name: Run Trunk Fmt
run: trunk fmt
- name: Get release version string
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Commit and push changes
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add .
git commit -m "Add firmware version ${{ steps.version.outputs.version }}"
git commit -m "Add firmware version ${{ steps.version.outputs.long }}"
git push
- name: Comment on PR

View File

@ -12,6 +12,7 @@ jobs:
submodules: true
- name: Update submodule
if: ${{ github.ref == 'refs/heads/master' }}
run: |
git submodule update --remote protobufs

6
.gitignore vendored
View File

@ -1,4 +1,8 @@
.pio
pio
pio.tar
web
web.tar
# ignore vscode IDE settings files
.vscode/*
@ -30,4 +34,4 @@ release/
.vscode/extensions.json
/compile_commands.json
src/mesh/raspihttp/certificate.pem
src/mesh/raspihttp/private_key.pem
src/mesh/raspihttp/private_key.pem

View File

@ -25,7 +25,7 @@ lib_deps =
${networking_base.lib_deps}
${radiolib_base.lib_deps}
rweather/Crypto@^0.4.0
https://github.com/lovyan03/LovyanGFX.git#1401c28a47646fe00538d487adcb2eb3c72de805
lovyan03/LovyanGFX@^1.2.0
https://github.com/pine64/libch341-spi-userspace#a9b17e3452f7fb747000d9b4ad4409155b39f6ef
build_flags =
@ -40,4 +40,4 @@ build_flags =
-lgpiod
-lyaml-cpp
-li2c
-std=c++17
-std=c++17

View File

@ -7,12 +7,12 @@ platform_packages = framework-arduinopico@https://github.com/earlephilhower/ardu
board_build.core = earlephilhower
board_build.filesystem_size = 0.5m
build_flags =
${arduino_base.build_flags} -Wno-unused-variable
${arduino_base.build_flags} -Wno-unused-variable -Wcast-align
-Isrc/platform/rp2xx0
-D__PLAT_RP2040__
-D__PLAT_RP2350__
# -D _POSIX_THREADS
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<modules/esp32> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/> -<mesh/wifi/> -<mesh/http/> -<mesh/raspihttp>
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<modules/esp32> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/> -<mesh/wifi/> -<mesh/http/> -<mesh/raspihttp> -<platform/rp2xx0/pico_sleep> -<platform/rp2xx0/hardware_rosc>
lib_ignore =
BluetoothOTA

1
bin/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
config.yaml

View File

@ -12,12 +12,6 @@ Lora:
# IRQ: 17
# Reset: 22
# Module: RF95 # Adafruit RFM9x
# Reset: 25
# CS: 7
# IRQ: 22
# Busy: 23
# Module: RF95 # Elecrow Lora RFM95 IOT https://www.elecrow.com/lora-rfm95-iot-board-for-rpi.html
# Reset: 22
# CS: 7
@ -29,6 +23,47 @@ Lora:
# Busy: 20
# Reset: 18
### The Radxa Zero 3E/W employs multiple gpio chips.
### Each gpio pin must be unique, but can be assigned to a specific gpio chip and line.
### In case solely a no. is given, the default gpio chip and pin == line will be employed.
###
# Module: sx1262 # Radxa Zero 3E/W + Ebyte E22-900M30S
# DIO2_AS_RF_SWITCH: true
# DIO3_TCXO_VOLTAGE: 1.8
# CS: # NSS PIN_24 -> chip 4, line 22
# pin: 24
# gpiochip: 4
# line: 22
# SCK: # SCK PIN_23 -> chip 4, line 18
# pin: 23
# gpiochip: 4
# line: 18
# Busy: # BUSY PIN_29 -> chip 3!, line 11
# pin: 29
# gpiochip: 3
# line: 11
# MOSI: # MOSI PIN_19 -> chip 4, line 19
# pin: 19
# gpiochip: 4
# line: 19
# MISO: # MISO PIN_21 -> chip 4, line 21
# pin: 21
# gpiochip: 4
# line: 21
# Reset: # NRST PIN_27 -> chip 4, line 10
# pin: 27
# gpiochip: 4
# line: 10
# IRQ: # DIO1 PIN_28 -> chip 4, line 11
# pin: 28
# gpiochip: 4
# line: 11
# RXen: # RXEN PIN_22 -> chip 3!, line 17
# pin: 22
# gpiochip: 3
# line: 17
# TXen: RADIOLIB_NC # TXEN no PIN, no line, fallback to default gpio chip
# Module: sx1268 # SX1268-based modules, tested with Ebyte E22 400M33S
# CS: 21
# IRQ: 16
@ -45,7 +80,7 @@ Lora:
# spiSpeed: 2000000
### Set gpio chip to use in /dev/. Defaults to 0.
### Set default/fallback gpio chip to use in /dev/. Defaults to 0.
### Notably the Raspberry Pi 5 puts the GPIO header on gpiochip4
# gpiochip: 4
@ -153,4 +188,4 @@ General:
MaxMessageQueue: 100
ConfigDirectory: /etc/meshtasticd/config.d/
# MACAddress: AA:BB:CC:DD:EE:FF
# MACAddressSource: eth0
# MACAddressSource: eth0

View File

@ -0,0 +1,5 @@
# Module: RF95 # Adafruit RFM9x
# Reset: 25
# CS: 7
# IRQ: 22
# Busy: 23

View File

@ -73,7 +73,7 @@ shift "$((OPTIND - 1))"
if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
$ESPTOOL_CMD erase_flash
$ESPTOOL_CMD write_flash 0x00 ${FILENAME}
$ESPTOOL_CMD write_flash 0x00 "${FILENAME}"
# Account for S3 board's different OTA partition
if [ -n "${FILENAME##*"s3"*}" ] && [ -n "${FILENAME##*"-v3"*}" ] && [ -n "${FILENAME##*"t-deck"*}" ] && [ -n "${FILENAME##*"wireless-paper"*}" ] && [ -n "${FILENAME##*"wireless-tracker"*}" ] && [ -n "${FILENAME##*"station-g2"*}" ] && [ -n "${FILENAME##*"unphone"*}" ]; then
if [ -n "${FILENAME##*"esp32c3"*}" ]; then

View File

@ -1,6 +1,8 @@
import configparser
import subprocess
import os
run_number = os.getenv('GITHUB_RUN_NUMBER', '0')
build_location = os.getenv('BUILD_LOCATION', 'local')
def readProps(prefsLoc):
"""Read the version of our project as a string"""
@ -11,6 +13,7 @@ def readProps(prefsLoc):
verObj = dict(
short="{}.{}.{}".format(version["major"], version["minor"], version["build"]),
long="unset",
deb="unset",
)
# Try to find current build SHA if if the workspace is clean. This could fail if git is not installed
@ -27,16 +30,16 @@ def readProps(prefsLoc):
# if isDirty:
# # short for 'dirty', we want to keep our verstrings source for protobuf reasons
# suffix = sha + "-d"
verObj["long"] = "{}.{}.{}.{}".format(
version["major"], version["minor"], version["build"], suffix
)
verObj["long"] = "{}.{}".format(verObj["short"], suffix)
verObj["deb"] = "{}.{}~{}{}".format(verObj["short"], run_number, build_location, sha)
except:
# print("Unexpected error:", sys.exc_info()[0])
# traceback.print_exc()
verObj["long"] = verObj["short"]
verObj["deb"] = "{}.{}~{}".format(verObj["short"], run_number, build_location)
# print("firmware version " + verStr)
return verObj
# print("path is" + ','.join(sys.path))
# print("path is" + ','.join(sys.path))

12
bin/rpkg.macros Normal file
View File

@ -0,0 +1,12 @@
function meshtastic_version {
meshtastic_version=$(python3 bin/buildinfo.py short)
echo -n "$meshtastic_version"
}
function git_commits_num {
total_commits=$(git rev-list --all --count)
echo -n "$total_commits"
}
function git_commit_sha {
commit_sha=$(git rev-parse --short HEAD)
echo -n "$commit_sha"
}

6
debian/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.debhelper
debhelper-build-stamp
meshtasticd
files
meshtasticd.substvars
meshtasticd.postrm.debhelper

7
debian/changelog vendored Normal file
View File

@ -0,0 +1,7 @@
meshtasticd (2.5.20.0) UNRELEASED; urgency=medium
* Initial packaging
* GitHub Actions Automatic version bump
* GitHub Actions Automatic version bump
-- Austin Lane <github-actions[bot]@users.noreply.github.com> Wed, 15 Jan 2025 14:08:54 +0000

7
debian/ci_changelog.sh vendored Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/bash
export DEBEMAIL="github-actions[bot]@users.noreply.github.com"
PKG_VERSION=$(python3 bin/buildinfo.py short)
dch --newversion "$PKG_VERSION.0" \
--distribution UNRELEASED \
"GitHub Actions Automatic version bump"

23
debian/ci_pack_sdeb.sh vendored Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/bash
export DEBEMAIL="jbennett@incomsystems.biz"
export PLATFORMIO_LIBDEPS_DIR=pio/libdeps
export PLATFORMIO_PACKAGES_DIR=pio/packages
export PLATFORMIO_CORE_DIR=pio/core
# Download libraries to `pio`
platformio pkg install -e native
platformio pkg install -e native -t platformio/tool-scons@4.40502.0
# Compress `pio` directory to prevent dh_clean from sanitizing it
tar -cf pio.tar pio/
rm -rf pio
# Download the latest meshtastic/web release build.tar to `web.tar`
curl -L https://github.com/meshtastic/web/releases/download/latest/build.tar -o web.tar
package=$(dpkg-parsechangelog --show-field Source)
rm -rf debian/changelog
dch --create --distribution "$SERIES" --package "$package" --newversion "$PKG_VERSION~$SERIES" \
"GitHub Actions Automatic packaging for $PKG_VERSION~$SERIES"
# Build the source deb
debuild -S -nc -k"$GPG_KEY_ID"

32
debian/control vendored Normal file
View File

@ -0,0 +1,32 @@
Source: meshtasticd
Section: misc
Priority: optional
Maintainer: Austin Lane <vidplace7@gmail.com>
Build-Depends: debhelper-compat (= 13),
tar,
gzip,
platformio,
python3-protobuf,
python3-grpcio,
git,
g++,
pkg-config,
libyaml-cpp-dev,
libgpiod-dev,
libbluetooth-dev,
libusb-1.0-0-dev,
libi2c-dev,
openssl,
libssl-dev,
libulfius-dev,
liborcania-dev
Standards-Version: 4.6.2
Homepage: https://github.com/meshtastic/firmware
Rules-Requires-Root: no
Package: meshtasticd
Architecture: any
Depends: ${misc:Depends}, ${shlibs:Depends}
Description: Meshtastic daemon for communicating with Meshtastic devices
Meshtastic is an off-grid text communication platform that uses inexpensive
LoRa radios.

4
debian/meshtasticd.dirs vendored Normal file
View File

@ -0,0 +1,4 @@
etc/meshtasticd
etc/meshtasticd/config.d
etc/meshtasticd/available.d
usr/share/meshtasticd/web

8
debian/meshtasticd.install vendored Normal file
View File

@ -0,0 +1,8 @@
.pio/build/native/meshtasticd usr/sbin
bin/config.yaml etc/meshtasticd
bin/config.d/* etc/meshtasticd/available.d
bin/meshtasticd.service lib/systemd/system
web/* usr/share/meshtasticd/web

23
debian/rules vendored Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/make -f
# export DH_VERBOSE = 1
# Use the "dh" sequencer
%:
dh $@
# https://docs.platformio.org/en/latest/envvars.html
PIO_ENV:=\
PLATFORMIO_CORE_DIR=pio/core \
PLATFORMIO_LIBDEPS_DIR=pio/libdeps \
PLATFORMIO_PACKAGES_DIR=pio/packages
override_dh_auto_build:
# Extract tarballs within source deb
tar -xf pio.tar
mkdir -p web && tar -xf web.tar -C web
gunzip web/ -r
# Build with platformio
$(PIO_ENV) platformio run -e native
# Move the binary and default config to the correct name
mv .pio/build/native/program .pio/build/native/meshtasticd
cp bin/config-dist.yaml bin/config.yaml

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (native)

2
debian/source/include-binaries vendored Normal file
View File

@ -0,0 +1,2 @@
pio.tar
web.tar

1
debian/source/options vendored Normal file
View File

@ -0,0 +1 @@
extend-diff-ignore = "\.pio"

91
meshtasticd.spec.rpkg Normal file
View File

@ -0,0 +1,91 @@
# meshtasticd spec file for RPM-based distributions
#
# Build locally with:
# ```
# sudo dnf install rpkg-util
# rpkg local
# ```
#
# See:
# - https://docs.pagure.org/rpkg-util/v3/index.html
# - https://docs.fedoraproject.org/en-US/packaging-guidelines/Versioning/
Name: meshtasticd
# Version Ex: 2.5.19
Version: {{{ meshtastic_version }}}
# Release Ex: 9127.daily.gitd7f5f620.fc41
Release: {{{ git_commits_num }}}%{?copr_projectname:.%{copr_projectname}}.git{{{ git_commit_sha }}}%{?dist}
VCS: {{{ git_dir_vcs }}}
Summary: Meshtastic daemon for communicating with Meshtastic devices
License: GPL-3.0
URL: https://github.com/meshtastic/firmware
Source0: {{{ git_dir_pack }}}
Source1: https://github.com/meshtastic/web/releases/download/latest/build.tar
BuildRequires: systemd-rpm-macros
BuildRequires: python3-devel
BuildRequires: platformio
BuildRequires: python3dist(protobuf)
BuildRequires: python3dist(grpcio[protobuf])
BuildRequires: python3dist(grpcio-tools)
BuildRequires: git-core
BuildRequires: gcc-c++
BuildRequires: pkgconfig(yaml-cpp)
BuildRequires: pkgconfig(libgpiod)
BuildRequires: pkgconfig(bluez)
BuildRequires: pkgconfig(libusb-1.0)
BuildRequires: libi2c-devel
# Web components:
BuildRequires: pkgconfig(openssl)
BuildRequires: pkgconfig(liborcania)
BuildRequires: pkgconfig(libyder)
BuildRequires: pkgconfig(libulfius)
%description
Meshtastic daemon for controlling Meshtastic devices. Meshtastic is an off-grid
text communication platform that uses inexpensive LoRa radios.
%prep
{{{ git_dir_setup_macro }}}
# Unpack the web files
mkdir -p web
tar -xf %{SOURCE1} -C web
gzip -dr web
%build
# Use the “native” environment from platformio to build a Linux binary
platformio run -e native
%install
mkdir -p %{buildroot}%{_sbindir}
install -m 0755 .pio/build/native/program %{buildroot}%{_sbindir}/meshtasticd
mkdir -p %{buildroot}%{_sysconfdir}/meshtasticd
install -m 0644 bin/config-dist.yaml %{buildroot}%{_sysconfdir}/meshtasticd/config.yaml
mkdir -p %{buildroot}%{_sysconfdir}/meshtasticd/config.d
mkdir -p %{buildroot}%{_sysconfdir}/meshtasticd/available.d
cp -r bin/config.d/* %{buildroot}%{_sysconfdir}/meshtasticd/available.d
install -D -m 0644 bin/meshtasticd.service %{buildroot}%{_unitdir}/meshtasticd.service
# Install the web files under /usr/share/meshtasticd/web
mkdir -p %{buildroot}%{_datadir}/meshtasticd/web
cp -r web/* %{buildroot}%{_datadir}/meshtasticd/web
%files
%license LICENSE
%doc README.md
%{_sbindir}/meshtasticd
%dir %{_sysconfdir}/meshtasticd
%dir %{_sysconfdir}/meshtasticd/config.d
%dir %{_sysconfdir}/meshtasticd/available.d
%config(noreplace) %{_sysconfdir}/meshtasticd/config.yaml
%config %{_sysconfdir}/meshtasticd/available.d/*
%{_unitdir}/meshtasticd.service
%dir %{_datadir}/meshtasticd
%dir %{_datadir}/meshtasticd/web
%{_datadir}/meshtasticd/web/*
%changelog
%autochangelog

View File

@ -3,42 +3,7 @@
[platformio]
default_envs = tbeam
;default_envs = pico
;default_envs = tbeam-s3-core
;default_envs = tbeam0.7
;default_envs = heltec-v1
;default_envs = heltec-v2_0
;default_envs = heltec-v2_1
;default_envs = heltec-wireless-tracker
;default_envs = chatter2
;default_envs = tlora-v1
;default_envs = tlora_v1_3
;default_envs = tlora-v2
;default_envs = tlora-v2-1-1_6
;default_envs = tlora-v2-1-1_6-tcxo
;default_envs = tlora-v3-3-0-tcxo
;default_envs = tlora-t3s3-v1
;default_envs = t-echo
;default_envs = canaryone
;default_envs = native
;default_envs = nano-g1
;default_envs = pca10059_diy_eink
;default_envs = meshtastic-diy-v1
;default_envs = meshtastic-diy-v1_1
;default_envs = meshtastic-dr-dev
;default_envs = m5stack-coreink
;default_envs = rak4631
;default_envs = rak4631_eth_gw
;default_envs = rak2560
;default_envs = rak_wismeshtap
;default_envs = wio-e5
;default_envs = radiomaster_900_bandit_nano
;default_envs = radiomaster_900_bandit_micro
;default_envs = radiomaster_900_bandit
;default_envs = heltec_vision_master_t190
;default_envs = heltec_vision_master_e213
;default_envs = heltec_vision_master_e290
;default_envs = heltec_mesh_node_t114
extra_configs =
arch/*/*.ini
variants/*/platformio.ini
@ -55,7 +20,7 @@ extra_scripts = bin/platformio-custom.py
build_flags = -Wno-missing-field-initializers
-Wno-format
-Isrc -Isrc/mesh -Isrc/mesh/generated -Isrc/gps -Isrc/buzz -Wl,-Map,.pio/build/output.map
-Isrc -Isrc/mesh -Isrc/mesh/generated -Isrc/gps -Isrc/buzz -Wl,-Map,${platformio.build_dir}/output.map
-DUSE_THREAD_NAMES
-DTINYGPS_OPTION_NO_CUSTOM_FIELDS
-DPB_ENABLE_MALLOC=1
@ -85,7 +50,7 @@ build_flags = -Wno-missing-field-initializers
-DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
#-DBUILD_EPOCH=$UNIX_TIME
;-D OLED_PL
#-D OLED_PL=1
monitor_speed = 115200
monitor_filters = direct
@ -124,7 +89,7 @@ lib_deps =
[radiolib_base]
lib_deps =
jgromes/RadioLib@7.1.0
jgromes/RadioLib@7.1.2
; Common libs for environmental measurements in telemetry module
; (not included in native / portduino)
@ -162,7 +127,8 @@ lib_deps =
mprograms/QMC5883LCompass@1.2.3
dfrobot/DFRobot_RTU@1.0.3
https://github.com/meshtastic/DFRobot_LarkWeatherStation#4de3a9cadef0f6a5220a8a906cf9775b02b0040d
https://github.com/DFRobot/DFRobot_RainfallSensor#38fea5e02b40a5430be6dab39a99a6f6347d667e
robtillaart/INA226@0.6.0
; Health Sensor Libraries
sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2
sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2

@ -1 +1 @@
Subproject commit c55f120a9c1ce90c85e4826907a0b9bcb2d5f5a2
Subproject commit fde27e4ef0fcee967063ba353422ed5f9a1c4790

2
rpkg.conf Normal file
View File

@ -0,0 +1,2 @@
[rpkg]
user_macros = "${git_props:root}/bin/rpkg.macros"

View File

@ -190,6 +190,20 @@ int32_t ButtonThread::runOnce()
case 4:
digitalWrite(PIN_EINK_EN, digitalRead(PIN_EINK_EN) == LOW);
break;
#endif
#if defined(RAK_4631)
// 5 clicks: start accelerometer/magenetometer calibration for 30 seconds
case 5:
if (accelerometerThread) {
accelerometerThread->calibrate(30);
}
break;
// 6 clicks: start accelerometer/magenetometer calibration for 60 seconds
case 6:
if (accelerometerThread) {
accelerometerThread->calibrate(60);
}
break;
#endif
// No valid multipress action
default:

View File

@ -9,6 +9,7 @@
*
*/
#include "FSCommon.h"
#include "SPILock.h"
#include "configuration.h"
#ifdef HAS_SDCARD
@ -102,6 +103,8 @@ bool copyFile(const char *from, const char *to)
return true;
#elif defined(FSCom)
// take SPI Lock
concurrency::LockGuard g(spiLock);
unsigned char cbuffer[16];
File f1 = FSCom.open(from, FILE_O_READ);
@ -145,16 +148,23 @@ bool renameFile(const char *pathFrom, const char *pathTo)
return false;
}
#elif defined(FSCom)
#ifdef ARCH_ESP32
// take SPI Lock
spiLock->lock();
// rename was fixed for ESP32 IDF LittleFS in April
return FSCom.rename(pathFrom, pathTo);
bool result = FSCom.rename(pathFrom, pathTo);
spiLock->unlock();
return result;
#else
// copyFile does its own locking.
if (copyFile(pathFrom, pathTo) && FSCom.remove(pathFrom)) {
return true;
} else {
return false;
}
#endif
#endif
}
@ -164,6 +174,7 @@ bool renameFile(const char *pathFrom, const char *pathTo)
* @brief Get the list of files in a directory.
*
* This function returns a list of files in a directory. The list includes the full path of each file.
* We can't use SPILOCK here because of recursion. Callers of this function should use SPILOCK.
*
* @param dirname The name of the directory.
* @param levels The number of levels of subdirectories to list.
@ -192,7 +203,7 @@ std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels)
file.close();
}
} else {
meshtastic_FileInfo fileInfo = {"", file.size()};
meshtastic_FileInfo fileInfo = {"", static_cast<uint32_t>(file.size())};
#ifdef ARCH_ESP32
strcpy(fileInfo.file_name, file.path());
#else
@ -212,6 +223,7 @@ std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels)
/**
* Lists the contents of a directory.
* We can't use SPILOCK here because of recursion. Callers of this function should use SPILOCK.
*
* @param dirname The name of the directory to list.
* @param levels The number of levels of subdirectories to list.
@ -325,18 +337,21 @@ void listDir(const char *dirname, uint8_t levels, bool del)
void rmDir(const char *dirname)
{
#ifdef FSCom
#if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
listDir(dirname, 10, true);
#elif defined(ARCH_NRF52)
// nRF52 implementation of LittleFS has a recursive delete function
FSCom.rmdir_r(dirname);
#endif
#endif
}
void fsInit()
{
#ifdef FSCom
spiLock->lock();
if (!FSBegin()) {
LOG_ERROR("Filesystem mount failed");
// assert(0); This auto-formats the partition, so no need to fail here.
@ -347,6 +362,7 @@ void fsInit()
LOG_DEBUG("Filesystem files:");
#endif
listDir("/", 10);
spiLock->unlock();
#endif
}
@ -356,6 +372,7 @@ void fsInit()
void setupSDCard()
{
#ifdef HAS_SDCARD
concurrency::LockGuard g(spiLock);
SDHandler.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
if (!SD.begin(SDCARD_CS, SDHandler)) {
@ -383,4 +400,4 @@ void setupSDCard()
LOG_DEBUG("Total space: %lu MB", (uint32_t)(SD.totalBytes() / (1024 * 1024)));
LOG_DEBUG("Used space: %lu MB", (uint32_t)(SD.usedBytes() / (1024 * 1024)));
#endif
}
}

View File

@ -5,6 +5,14 @@
// Only way to work on both esp32 and nrf52
static File openFile(const char *filename, bool fullAtomic)
{
concurrency::LockGuard g(spiLock);
LOG_DEBUG("Opening %s, fullAtomic=%d", filename, fullAtomic);
#ifdef ARCH_NRF52
lfs_assert_failed = false;
File file = FSCom.open(filename, FILE_O_WRITE);
file.seek(0);
return file;
#endif
if (!fullAtomic)
FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists)
@ -13,7 +21,6 @@ static File openFile(const char *filename, bool fullAtomic)
// clear any previous LFS errors
lfs_assert_failed = false;
return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE);
}
@ -53,14 +60,26 @@ bool SafeFile::close()
if (!f)
return false;
spiLock->lock();
#ifdef ARCH_NRF52
f.truncate();
#endif
f.close();
spiLock->unlock();
#ifdef ARCH_NRF52
return true;
#endif
if (!testReadback())
return false;
// brief window of risk here ;-)
if (fullAtomic && FSCom.exists(filename.c_str()) && !FSCom.remove(filename.c_str())) {
LOG_ERROR("Can't remove old pref file");
return false;
{ // Scope for lock
concurrency::LockGuard g(spiLock);
// brief window of risk here ;-)
if (fullAtomic && FSCom.exists(filename.c_str()) && !FSCom.remove(filename.c_str())) {
LOG_ERROR("Can't remove old pref file");
return false;
}
}
String filenameTmp = filename;
@ -76,6 +95,7 @@ bool SafeFile::close()
/// Read our (closed) tempfile back in and compare the hash
bool SafeFile::testReadback()
{
concurrency::LockGuard g(spiLock);
bool lfs_failed = lfs_assert_failed;
lfs_assert_failed = false;

View File

@ -1,6 +1,7 @@
#pragma once
#include "FSCommon.h"
#include "SPILock.h"
#include "configuration.h"
#ifdef FSCom

View File

@ -145,6 +145,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define OPT3001_ADDR_ALT 0x44
#define MLX90632_ADDR 0x3A
#define DFROBOT_LARK_ADDR 0x42
#define DFROBOT_RAIN_ADDR 0x1d
#define NAU7802_ADDR 0x2A
#define MAX30102_ADDR 0x57
#define MLX90614_ADDR_DEF 0x5A
@ -178,13 +179,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define TCA9535_ADDR 0x20
#define TCA9555_ADDR 0x26
// -----------------------------------------------------------------------------
// GPS
// -----------------------------------------------------------------------------
#ifndef GPS_THREAD_INTERVAL
#define GPS_THREAD_INTERVAL 200
#endif
// -----------------------------------------------------------------------------
// Touchscreen
// -----------------------------------------------------------------------------
@ -206,6 +200,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define VEXT_ON_VALUE LOW
#endif
// -----------------------------------------------------------------------------
// GPS
// -----------------------------------------------------------------------------
#ifndef GPS_BAUDRATE
#define GPS_BAUDRATE 9600
#define GPS_BAUDRATE_FIXED 0
@ -213,6 +211,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define GPS_BAUDRATE_FIXED 1
#endif
#ifndef GPS_THREAD_INTERVAL
#define GPS_THREAD_INTERVAL 200
#endif
/* Step #2: follow with defines common to the architecture;
also enable HAS_ option not specifically disabled by variant.h */
#include "architecture.h"

View File

@ -66,6 +66,7 @@ class ScanI2C
CGRADSENS,
INA226,
NXP_SE050,
DFROBOT_RAIN,
} DeviceType;
// typedef uint8_t DeviceAddress;

View File

@ -84,23 +84,33 @@ ScanI2C::DeviceType ScanI2CTwoWire::probeOLED(ScanI2C::DeviceAddress addr) const
return o_probe;
}
uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation &registerLocation,
ScanI2CTwoWire::ResponseWidth responseWidth) const
ScanI2CTwoWire::ResponseWidth responseWidth, bool zeropad = false) const
{
uint16_t value = 0x00;
TwoWire *i2cBus = fetchI2CBus(registerLocation.i2cAddress);
i2cBus->beginTransmission(registerLocation.i2cAddress.address);
i2cBus->write(registerLocation.registerAddress);
if (zeropad) {
// Lark Commands need the argument list length in 2 bytes.
i2cBus->write((int)0);
i2cBus->write((int)0);
}
i2cBus->endTransmission();
delay(20);
i2cBus->requestFrom(registerLocation.i2cAddress.address, responseWidth);
if (i2cBus->available() == 2) {
if (i2cBus->available() > 1) {
// Read MSB, then LSB
value = (uint16_t)i2cBus->read() << 8;
value |= i2cBus->read();
} else if (i2cBus->available()) {
value = i2cBus->read();
}
// Drain excess bytes
for (uint8_t i = 0; i < responseWidth - 1; i++) {
if (i2cBus->available())
i2cBus->read();
}
return value;
}
@ -286,7 +296,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
RESPONSE_PAYLOAD 0x01
RESPONSE_PAYLOAD+1 0x00
*/
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x05), 2);
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x05), 6, true);
LOG_DEBUG("Register MFG_UID 05: 0x%x", registerValue);
if (registerValue == 0x5305) {
logFoundDevice("DFRobot Lark", (uint8_t)addr.address);
@ -402,6 +412,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(MLX90632_ADDR, MLX90632, "MLX90632", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(NAU7802_ADDR, NAU7802, "NAU7802", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(MAX1704X_ADDR, MAX17048, "MAX17048", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
#ifdef HAS_TPS65233
SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233", (uint8_t)addr.address);
#endif

View File

@ -53,7 +53,7 @@ class ScanI2CTwoWire : public ScanI2C
concurrency::Lock lock;
uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth) const;
uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth, bool) const;
DeviceType probeOLED(ScanI2C::DeviceAddress) const;

View File

@ -35,7 +35,11 @@ template <typename T, std::size_t N> std::size_t array_count(const T (&)[N])
}
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
#if defined(RAK2560)
HardwareSerial *GPS::_serial_gps = &Serial2;
#else
HardwareSerial *GPS::_serial_gps = &Serial1;
#endif
#elif defined(ARCH_RP2040)
SerialUART *GPS::_serial_gps = &Serial1;
#else
@ -1190,6 +1194,8 @@ GnssModel_t GPS::probe(int serialSpeed)
PROBE_SIMPLE("L76B", "$PMTK605*31", "Quectel-L76B", GNSS_MODEL_MTK_L76B, 500);
PROBE_SIMPLE("PA1616S", "$PMTK605*31", "1616S", GNSS_MODEL_MTK_PA1616S, 500);
PROBE_SIMPLE("LS20031", "$PMTK605*31", "MC-1513", GNSS_MODEL_LS20031, 500);
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
UBXChecksum(cfg_rate, sizeof(cfg_rate));
clearBuffer();
@ -1750,4 +1756,4 @@ void GPS::toggleGpsMode()
enable();
}
}
#endif // Exclude GPS
#endif // Exclude GPS

View File

@ -29,7 +29,8 @@ typedef enum {
GNSS_MODEL_MTK_L76B,
GNSS_MODEL_MTK_PA1616S,
GNSS_MODEL_AG3335,
GNSS_MODEL_AG3352
GNSS_MODEL_AG3352,
GNSS_MODEL_LS20031
} GnssModel_t;
typedef enum {
@ -239,4 +240,4 @@ class GPS : private concurrency::OSThread
};
extern GPS *gps;
#endif // Exclude GPS
#endif // Exclude GPS

View File

@ -1506,7 +1506,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#elif defined(USE_ST7567)
dispdev = new ST7567Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif ARCH_PORTDUINO
#elif ARCH_PORTDUINO && !HAS_TFT
if (settingsMap[displayPanel] != no_screen) {
LOG_DEBUG("Make TFTDisplay!");
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
@ -2756,4 +2756,4 @@ int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
} // namespace graphics
#else
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
#endif // HAS_SCREEN
#endif // HAS_SCREEN

View File

@ -278,6 +278,10 @@ class Screen : public concurrency::OSThread
bool hasHeading() { return hasCompass; }
long getHeading() { return compassHeading; }
void setEndCalibration(uint32_t _endCalibrationAt) { endCalibrationAt = _endCalibrationAt; }
uint32_t getEndCalibration() { return endCalibrationAt; }
// functions for display brightness
void increaseBrightness();
void decreaseBrightness();
@ -673,6 +677,8 @@ class Screen : public concurrency::OSThread
bool hasCompass = false;
float compassHeading;
uint32_t endCalibrationAt;
/// Holds state for debug information
DebugInfo debugInfo;

View File

@ -16,49 +16,61 @@
#include "graphics/fonts/OLEDDisplayFontsCS.h"
#endif
#ifdef OLED_PL
#define FONT_SMALL_LOCAL ArialMT_Plain_10_PL
#else
#ifdef OLED_RU
#define FONT_SMALL_LOCAL ArialMT_Plain_10_RU
#else
#ifdef OLED_UA
#define FONT_SMALL_LOCAL ArialMT_Plain_10_UA // Height: 13
#else
#ifdef OLED_CS
#define FONT_SMALL_LOCAL ArialMT_Plain_10_CS
#else
#define FONT_SMALL_LOCAL ArialMT_Plain_10 // Height: 13
#endif
#endif
#endif
#endif
#ifdef OLED_PL
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_PL // Height: 19
#else
#ifdef OLED_UA
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_UA // Height: 19
#else
#ifdef OLED_CS
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_CS
#else
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16 // Height: 19
#endif
#endif
#endif
#ifdef OLED_PL
#define FONT_LARGE_LOCAL ArialMT_Plain_24_PL // Height: 28
#else
#ifdef OLED_UA
#define FONT_LARGE_LOCAL ArialMT_Plain_24_UA // Height: 28
#else
#ifdef OLED_CS
#define FONT_LARGE_LOCAL ArialMT_Plain_24_CS // Height: 28
#else
#define FONT_LARGE_LOCAL ArialMT_Plain_24 // Height: 28
#endif
#endif
#endif
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
// The screen is bigger so use bigger fonts
#define FONT_SMALL ArialMT_Plain_16 // Height: 19
#define FONT_MEDIUM ArialMT_Plain_24 // Height: 28
#define FONT_LARGE ArialMT_Plain_24 // Height: 28
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
#define FONT_MEDIUM FONT_LARGE_LOCAL // Height: 28
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
#else
#ifdef OLED_PL
#define FONT_SMALL ArialMT_Plain_10_PL
#else
#ifdef OLED_RU
#define FONT_SMALL ArialMT_Plain_10_RU
#else
#ifdef OLED_UA
#define FONT_SMALL ArialMT_Plain_10_UA // Height: 13
#else
#ifdef OLED_CS
#define FONT_SMALL ArialMT_Plain_10_CS
#else
#define FONT_SMALL ArialMT_Plain_10 // Height: 13
#endif
#endif
#endif
#endif
#ifdef OLED_UA
#define FONT_MEDIUM ArialMT_Plain_16_UA // Height: 19
#else
#ifdef OLED_CS
#define FONT_MEDIUM ArialMT_Plain_16_CS
#else
#define FONT_MEDIUM ArialMT_Plain_16 // Height: 19
#endif
#endif
#ifdef OLED_UA
#define FONT_LARGE ArialMT_Plain_24_UA // Height: 28
#else
#ifdef OLED_CS
#define FONT_LARGE ArialMT_Plain_24_CS // Height: 28
#else
#define FONT_LARGE ArialMT_Plain_24 // Height: 28
#endif
#endif
#define FONT_SMALL FONT_SMALL_LOCAL // Height: 13
#define FONT_MEDIUM FONT_MEDIUM_LOCAL // Height: 19
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
#endif
#define _fontHeight(font) ((font)[1] + 1) // height is position 1

View File

@ -352,7 +352,7 @@ static TFT_eSPI *tft = nullptr; // Invoke library, pins defined in User_Setup.h
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_LCD *_panel_instance;
lgfx::Panel_Device *_panel_instance;
lgfx::Bus_SPI _bus_instance;
lgfx::ITouch *_touch_instance;
@ -366,10 +366,21 @@ class LGFX : public lgfx::LGFX_Device
_panel_instance = new lgfx::Panel_ST7735;
else if (settingsMap[displayPanel] == st7735s)
_panel_instance = new lgfx::Panel_ST7735S;
else if (settingsMap[displayPanel] == st7796)
_panel_instance = new lgfx::Panel_ST7796;
else if (settingsMap[displayPanel] == ili9341)
_panel_instance = new lgfx::Panel_ILI9341;
else if (settingsMap[displayPanel] == ili9342)
_panel_instance = new lgfx::Panel_ILI9342;
else if (settingsMap[displayPanel] == ili9488)
_panel_instance = new lgfx::Panel_ILI9488;
else if (settingsMap[displayPanel] == hx8357d)
_panel_instance = new lgfx::Panel_HX8357D;
else {
_panel_instance = new lgfx::Panel_NULL;
LOG_ERROR("Unknown display panel configured!");
}
auto buscfg = _bus_instance.config();
buscfg.spi_mode = 0;
buscfg.spi_host = settingsMap[displayspidev];
@ -383,12 +394,12 @@ class LGFX : public lgfx::LGFX_Device
LOG_DEBUG("Height: %d, Width: %d ", settingsMap[displayHeight], settingsMap[displayWidth]);
cfg.pin_cs = settingsMap[displayCS]; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = settingsMap[displayReset];
cfg.panel_width = settingsMap[displayWidth]; // actual displayable width
cfg.panel_height = settingsMap[displayHeight]; // actual displayable height
cfg.offset_x = settingsMap[displayOffsetX]; // Panel offset amount in X direction
cfg.offset_y = settingsMap[displayOffsetY]; // Panel offset amount in Y direction
cfg.offset_rotation = 0; // Rotation direction value offset 0~7 (4~7 is mirrored)
cfg.invert = settingsMap[displayInvert]; // Set to true if the light/darkness of the panel is reversed
cfg.panel_width = settingsMap[displayWidth]; // actual displayable width
cfg.panel_height = settingsMap[displayHeight]; // actual displayable height
cfg.offset_x = settingsMap[displayOffsetX]; // Panel offset amount in X direction
cfg.offset_y = settingsMap[displayOffsetY]; // Panel offset amount in Y direction
cfg.offset_rotation = settingsMap[displayOffsetRotate]; // Rotation direction value offset 0~7 (4~7 is mirrored)
cfg.invert = settingsMap[displayInvert]; // Set to true if the light/darkness of the panel is reversed
_panel_instance->config(cfg);
@ -410,7 +421,7 @@ class LGFX : public lgfx::LGFX_Device
touch_cfg.y_max = settingsMap[displayWidth] - 1;
touch_cfg.pin_int = settingsMap[touchscreenIRQ];
touch_cfg.bus_shared = true;
touch_cfg.offset_rotation = 1;
touch_cfg.offset_rotation = settingsMap[touchscreenRotate];
if (settingsMap[touchscreenI2CAddr] != -1) {
touch_cfg.i2c_addr = settingsMap[touchscreenI2CAddr];
} else {

View File

@ -7,7 +7,7 @@ const uint8_t ArialMT_Plain_10_CS[] PROGMEM = {
0x20, // First char: 32
0xE0, // Number of chars: 224
// Jump Table:
0xFF, 0xFF, 0x00, 0x0A, // 32
0xFF, 0xFF, 0x00, 0x03, // 32
0x00, 0x00, 0x04, 0x03, // 33
0x00, 0x04, 0x05, 0x04, // 34
0x00, 0x09, 0x09, 0x06, // 35
@ -453,7 +453,7 @@ const uint8_t ArialMT_Plain_16_CS[] PROGMEM = {
0x20, // First char: 32
0xE0, // Number of chars: 224
// Jump Table:
0xFF, 0xFF, 0x00, 0x10, // 32
0xFF, 0xFF, 0x00, 0x04, // 32
0x00, 0x00, 0x08, 0x04, // 33
0x00, 0x08, 0x0D, 0x06, // 34
0x00, 0x15, 0x1A, 0x0A, // 35
@ -1036,7 +1036,7 @@ const uint8_t ArialMT_Plain_24_CS[] PROGMEM = {
0x20, // First char: 32
0xE0, // Number of chars: 224
// Jump Table:
0xFF, 0xFF, 0x00, 0x18, // 32
0xFF, 0xFF, 0x00, 0x06, // 32
0x00, 0x00, 0x13, 0x06, // 33
0x00, 0x13, 0x1A, 0x08, // 34
0x00, 0x2D, 0x33, 0x0E, // 35

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@
#elif __MBED__
#define PROGMEM
#endif
extern const uint8_t ArialMT_Plain_10_PL[] PROGMEM;
extern const uint8_t ArialMT_Plain_16_PL[] PROGMEM;
extern const uint8_t ArialMT_Plain_24_PL[] PROGMEM;
#endif

View File

@ -7,6 +7,9 @@
#include "ScanAndSelect.h"
#include "modules/CannedMessageModule.h"
#include <Throttle.h>
#ifdef ARCH_PORTDUINO // Only to check for pin conflict with user button
#include "platform/portduino/PortduinoGlue.h"
#endif
// Config
static const char name[] = "scanAndSelect"; // should match "allow input source" string
@ -30,7 +33,9 @@ bool ScanAndSelectInput::init()
if (strcasecmp(moduleConfig.canned_message.allow_input_source, name) != 0)
return false;
// Use any available inputbroker pin as the button
// Determine which pin to use for the single scan-and-select button
// User can specify this by setting any of the inputbroker pins
// If all values are zero, we'll assume the user *does* want GPIO0
if (moduleConfig.canned_message.inputbroker_pin_press)
pin = moduleConfig.canned_message.inputbroker_pin_press;
else if (moduleConfig.canned_message.inputbroker_pin_a)
@ -38,7 +43,25 @@ bool ScanAndSelectInput::init()
else if (moduleConfig.canned_message.inputbroker_pin_b)
pin = moduleConfig.canned_message.inputbroker_pin_b;
else
return false; // Short circuit: no button found
pin = 0; // GPIO 0 then
// Short circuit: if selected pin conficts with the user button
#if defined(ARCH_PORTDUINO)
int pinUserButton = 0;
if (settingsMap.count(user) != 0) {
pinUserButton = settingsMap[user];
}
#elif defined(USERPREFS_BUTTON_PIN)
int pinUserButton = config.device.button_gpio ? config.device.button_gpio : USERPREFS_BUTTON_PIN;
#elif defined(BUTTON_PIN)
int pinUserButton = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN;
#else
int pinUserButton = config.device.button_gpio;
#endif
if (pin == pinUserButton) {
LOG_ERROR("ScanAndSelect conflict with user button");
return false;
}
// Set-up the button
pinMode(pin, INPUT_PULLUP);

View File

@ -92,6 +92,7 @@ NRF52Bluetooth *nrf52Bluetooth = nullptr;
#include "mesh/raspihttp/PiWebServer.h"
#include "platform/portduino/PortduinoGlue.h"
#include "platform/portduino/USBHal.h"
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
@ -366,6 +367,8 @@ void setup()
#endif
#endif
initSPI();
OSThread::setup();
ledPeriodic = new Periodic("Blink", ledBlinker);
@ -609,6 +612,7 @@ void setup()
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::ICM20948, meshtastic_TelemetrySensorType_ICM20948);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::MAX30102, meshtastic_TelemetrySensorType_MAX30102);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::CGRADSENS, meshtastic_TelemetrySensorType_RADSENS);
scannerToSensorsMap(i2cScanner, ScanI2C::DeviceType::DFROBOT_RAIN, meshtastic_TelemetrySensorType_DFROBOT_RAIN);
i2cScanner.reset();
#endif
@ -642,8 +646,6 @@ void setup()
rp2040Setup();
#endif
initSPI(); // needed here before reading from littleFS
// We do this as early as possible because this loads preferences from flash
// but we need to do this after main cpu init (esp32setup), because we need the random seed set
nodeDB = new NodeDB;
@ -827,116 +829,56 @@ void setup()
#endif
#ifdef ARCH_PORTDUINO
if (settingsMap[use_sx1262]) {
if (!rIf) {
LOG_DEBUG("Activate sx1262 radio on SPI port %s", settingsStrings[spidev].c_str());
const struct {
configNames cfgName;
std::string strName;
} loraModules[] = {{use_rf95, "RF95"}, {use_sx1262, "sx1262"}, {use_sx1268, "sx1268"}, {use_sx1280, "sx1280"},
{use_lr1110, "lr1110"}, {use_lr1120, "lr1120"}, {use_lr1121, "lr1121"}, {use_llcc68, "LLCC68"}};
// as one can't use a function pointer to the class constructor:
auto loraModuleInterface = [](configNames cfgName, LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq,
RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy) {
switch (cfgName) {
case use_rf95:
return (RadioInterface *)new RF95Interface(hal, cs, irq, rst, busy);
case use_sx1262:
return (RadioInterface *)new SX1262Interface(hal, cs, irq, rst, busy);
case use_sx1268:
return (RadioInterface *)new SX1268Interface(hal, cs, irq, rst, busy);
case use_sx1280:
return (RadioInterface *)new SX1280Interface(hal, cs, irq, rst, busy);
case use_lr1110:
return (RadioInterface *)new LR1110Interface(hal, cs, irq, rst, busy);
case use_lr1120:
return (RadioInterface *)new LR1120Interface(hal, cs, irq, rst, busy);
case use_lr1121:
return (RadioInterface *)new LR1121Interface(hal, cs, irq, rst, busy);
case use_llcc68:
return (RadioInterface *)new LLCC68Interface(hal, cs, irq, rst, busy);
default:
assert(0); // shouldn't happen
return (RadioInterface *)nullptr;
}
};
for (auto &loraModule : loraModules) {
if (settingsMap[loraModule.cfgName] && !rIf) {
LOG_DEBUG("Activate %s radio on SPI port %s", loraModule.strName.c_str(), settingsStrings[spidev].c_str());
if (settingsStrings[spidev] == "ch341") {
RadioLibHAL = ch341Hal;
} else {
RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
}
rIf = new SX1262Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]);
rIf = loraModuleInterface(loraModule.cfgName, (LockingArduinoHal *)RadioLibHAL, settingsMap[cs_pin],
settingsMap[irq_pin], settingsMap[reset_pin], settingsMap[busy_pin]);
if (!rIf->init()) {
LOG_WARN("No SX1262 radio");
delete rIf;
exit(EXIT_FAILURE);
} else {
LOG_INFO("SX1262 init success");
}
}
} else if (settingsMap[use_rf95]) {
if (!rIf) {
LOG_DEBUG("Activate rf95 radio on SPI port %s", settingsStrings[spidev].c_str());
RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
rIf = new RF95Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]);
if (!rIf->init()) {
LOG_WARN("No RF95 radio");
LOG_WARN("No %s radio", loraModule.strName.c_str());
delete rIf;
rIf = NULL;
exit(EXIT_FAILURE);
} else {
LOG_INFO("RF95 init success");
}
}
} else if (settingsMap[use_sx1280]) {
if (!rIf) {
LOG_DEBUG("Activate sx1280 radio on SPI port %s", settingsStrings[spidev].c_str());
RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
rIf = new SX1280Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]);
if (!rIf->init()) {
LOG_WARN("No SX1280 radio");
delete rIf;
rIf = NULL;
exit(EXIT_FAILURE);
} else {
LOG_INFO("SX1280 init success");
}
}
} else if (settingsMap[use_lr1110]) {
if (!rIf) {
LOG_DEBUG("Activate lr1110 radio on SPI port %s", settingsStrings[spidev].c_str());
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
rIf = new LR1110Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]);
if (!rIf->init()) {
LOG_WARN("No LR1110 radio");
delete rIf;
rIf = NULL;
exit(EXIT_FAILURE);
} else {
LOG_INFO("LR1110 init success");
}
}
} else if (settingsMap[use_lr1120]) {
if (!rIf) {
LOG_DEBUG("Activate lr1120 radio on SPI port %s", settingsStrings[spidev].c_str());
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
rIf = new LR1120Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]);
if (!rIf->init()) {
LOG_WARN("No LR1120 radio");
delete rIf;
rIf = NULL;
exit(EXIT_FAILURE);
} else {
LOG_INFO("LR1120 init success");
}
}
} else if (settingsMap[use_lr1121]) {
if (!rIf) {
LOG_DEBUG("Activate lr1121 radio on SPI port %s", settingsStrings[spidev].c_str());
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
rIf = new LR1121Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]);
if (!rIf->init()) {
LOG_WARN("No LR1121 radio");
delete rIf;
rIf = NULL;
exit(EXIT_FAILURE);
} else {
LOG_INFO("LR1121 init success");
}
}
} else if (settingsMap[use_sx1268]) {
if (!rIf) {
LOG_DEBUG("Activate sx1268 radio on SPI port %s", settingsStrings[spidev].c_str());
RadioLibHAL = new LockingArduinoHal(SPI, spiSettings);
rIf = new SX1268Interface((LockingArduinoHal *)RadioLibHAL, settingsMap[cs], settingsMap[irq], settingsMap[reset],
settingsMap[busy]);
if (!rIf->init()) {
LOG_WARN("No SX1268 radio");
delete rIf;
rIf = NULL;
exit(EXIT_FAILURE);
} else {
LOG_INFO("SX1268 init success");
LOG_INFO("%s init success", loraModule.strName.c_str());
}
}
}
#elif defined(HW_SPI1_DEVICE)
LockingArduinoHal *RadioLibHAL = new LockingArduinoHal(SPI1, spiSettings);
#else // HW_SPI1_DEVICE
@ -1181,6 +1123,7 @@ void setup()
#if __has_include(<ulfius.h>)
if (settingsMap[webserverport] != -1) {
piwebServerThread = new PiWebServerThread();
std::atexit([] { delete piwebServerThread; });
}
#endif
initApiServer(TCPPort);

View File

@ -4,6 +4,7 @@
#include "NodeDB.h"
#include "configuration.h"
#include "modules/RoutingModule.h"
#include <algorithm>
#include <assert.h>
std::vector<MeshModule *> *MeshModule::modules;
@ -29,7 +30,9 @@ void MeshModule::setup() {}
MeshModule::~MeshModule()
{
assert(0); // FIXME - remove from list of modules once someone needs this feature
auto it = std::find(modules->begin(), modules->end(), this);
assert(it != modules->end());
modules->erase(it);
}
meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex,

View File

@ -69,7 +69,11 @@ bool MeshPacketQueue::enqueue(meshtastic_MeshPacket *p)
{
// no space - try to replace a lower priority packet in the queue
if (queue.size() >= maxLen) {
return replaceLowerPriorityPacket(p);
bool replaced = replaceLowerPriorityPacket(p);
if (!replaced) {
LOG_WARN("TX queue is full, and there is no lower-priority packet available to evict in favour of 0x%08x", p->id);
}
return replaced;
}
// Find the correct position using upper_bound to maintain a stable order
@ -113,7 +117,10 @@ meshtastic_MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id, bool t
return NULL;
}
/** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */
/**
* Attempt to find a lower-priority packet in the queue and replace it with the provided one.
* @return True if the replacement succeeded, false otherwise
*/
bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p)
{
@ -122,11 +129,12 @@ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p)
}
// Check if the packet at the back has a lower priority than the new packet
auto &backPacket = queue.back();
auto *backPacket = queue.back();
if (!backPacket->tx_after && backPacket->priority < p->priority) {
LOG_WARN("Dropping packet 0x%08x to make room in the TX queue for higher-priority packet 0x%08x", backPacket->id, p->id);
// Remove the back packet
packetPool.release(backPacket);
queue.pop_back();
packetPool.release(backPacket);
// Insert the new packet in the correct order
enqueue(p);
return true;
@ -139,8 +147,12 @@ bool MeshPacketQueue::replaceLowerPriorityPacket(meshtastic_MeshPacket *p)
for (; refPacket->tx_after && it != queue.begin(); refPacket = *--it)
;
if (!refPacket->tx_after && refPacket->priority < p->priority) {
LOG_WARN("Dropping non-late packet 0x%08x to make room in the TX queue for higher-priority packet 0x%08x",
refPacket->id, p->id);
queue.erase(it);
packetPool.release(refPacket);
enqueue(refPacket);
// Insert the new packet in the correct order
enqueue(p);
return true;
}
}

View File

@ -142,7 +142,7 @@ class MeshService
void sendToPhone(meshtastic_MeshPacket *p);
/// Send an MQTT message to the phone for client proxying
void sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage *m);
virtual void sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage *m);
/// Send a ClientNotification to the phone
void sendClientNotification(meshtastic_ClientNotification *cn);

View File

@ -13,6 +13,7 @@
#include "PowerFSM.h"
#include "RTC.h"
#include "Router.h"
#include "SPILock.h"
#include "SafeFile.h"
#include "TypeConversions.h"
#include "error.h"
@ -196,9 +197,8 @@ NodeDB::NodeDB()
uint32_t channelFileCRC = crc32Buffer(&channelFile, sizeof(channelFile));
int saveWhat = 0;
// bool hasUniqueId = false;
// Get device unique id
#if defined(ARCH_ESP32) && defined(ESP_EFUSE_OPTIONAL_UNIQUE_ID)
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3)
uint32_t unique_id[4];
// ESP32 factory burns a unique id in efuse for S2+ series and evidently C3+ series
// This is used for HMACs in the esp-rainmaker AIOT platform and seems to be a good choice for us
@ -206,7 +206,6 @@ NodeDB::NodeDB()
if (err == ESP_OK) {
memcpy(myNodeInfo.device_id.bytes, unique_id, sizeof(unique_id));
myNodeInfo.device_id.size = 16;
hasUniqueId = true;
} else {
LOG_WARN("Failed to read unique id from efuse");
}
@ -220,12 +219,12 @@ NodeDB::NodeDB()
memcpy(myNodeInfo.device_id.bytes + sizeof(device_id_start), &device_id_end, sizeof(device_id_end));
myNodeInfo.device_id.size = 16;
// Uncomment below to print the device id
// hasUniqueId = true;
#else
// FIXME - implement for other platforms
#endif
// if (hasUniqueId) {
// if (myNodeInfo.device_id.size == 16) {
// std::string deviceIdHex;
// for (size_t i = 0; i < myNodeInfo.device_id.size; ++i) {
// char buf[3];
@ -409,6 +408,13 @@ bool NodeDB::resetRadioConfig(bool factory_reset)
rebootAtMsec = millis() + (5 * 1000);
}
#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3)) && defined(HAS_TFT)
// as long as PhoneAPI shares BT and TFT app switch BT off
config.bluetooth.enabled = false;
if (moduleConfig.external_notification.nag_timeout == 60)
moduleConfig.external_notification.nag_timeout = 0;
#endif
return didFactoryReset;
}
@ -416,12 +422,15 @@ bool NodeDB::factoryReset(bool eraseBleBonds)
{
LOG_INFO("Perform factory reset!");
// first, remove the "/prefs" (this removes most prefs)
rmDir("/prefs");
spiLock->lock();
rmDir("/prefs"); // this uses spilock internally...
#ifdef FSCom
if (FSCom.exists("/static/rangetest.csv") && !FSCom.remove("/static/rangetest.csv")) {
LOG_ERROR("Could not remove rangetest.csv file");
}
#endif
spiLock->unlock();
// second, install default state (this will deal with the duplicate mac address issue)
installDefaultDeviceState();
installDefaultConfig(!eraseBleBonds); // Also preserve the private key if we're not erasing BLE bonds
@ -906,6 +915,7 @@ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t
{
LoadFileResult state = LoadFileResult::OTHER_FAILURE;
#ifdef FSCom
concurrency::LockGuard g(spiLock);
auto f = FSCom.open(filename, FILE_O_READ);
@ -939,12 +949,14 @@ void NodeDB::loadFromDisk()
// disk we will still factoryReset to restore things.
#ifdef ARCH_ESP32
spiLock->lock();
if (FSCom.exists("/static/static"))
rmDir("/static/static"); // Remove bad static web files bundle from initial 2.5.13 release
spiLock->unlock();
#endif
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES_FS * sizeof(meshtastic_NodeInfo),
auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES_FS * meshtastic_NodeInfoLite_size,
sizeof(meshtastic_DeviceState), &meshtastic_DeviceState_msg, &devicestate);
// See https://github.com/meshtastic/firmware/issues/4184#issuecomment-2269390786
@ -985,8 +997,11 @@ void NodeDB::loadFromDisk()
// Make sure we load hard coded admin keys even when the configuration file has none.
// Initialize admin_key_count to zero
byte numAdminKeys = 0;
#if defined(USERPREFS_USE_ADMIN_KEY_0) || defined(USERPREFS_USE_ADMIN_KEY_1) || defined(USERPREFS_USE_ADMIN_KEY_2)
uint16_t sum = 0;
#endif
#ifdef USERPREFS_USE_ADMIN_KEY_0
for (uint8_t b = 0; b < 32; b++) {
sum += config.security.admin_key[0].bytes[b];
}
@ -995,8 +1010,6 @@ void NodeDB::loadFromDisk()
LOG_INFO("Admin 0 key zero. Loading hard coded key from user preferences.");
memcpy(config.security.admin_key[0].bytes, userprefs_admin_key_0, 32);
config.security.admin_key[0].size = 32;
config.security.admin_key_count = numAdminKeys;
saveToDisk(SEGMENT_CONFIG);
}
#endif
@ -1010,8 +1023,6 @@ void NodeDB::loadFromDisk()
LOG_INFO("Admin 1 key zero. Loading hard coded key from user preferences.");
memcpy(config.security.admin_key[1].bytes, userprefs_admin_key_1, 32);
config.security.admin_key[1].size = 32;
config.security.admin_key_count = numAdminKeys;
saveToDisk(SEGMENT_CONFIG);
}
#endif
@ -1025,10 +1036,14 @@ void NodeDB::loadFromDisk()
LOG_INFO("Admin 2 key zero. Loading hard coded key from user preferences.");
memcpy(config.security.admin_key[2].bytes, userprefs_admin_key_2, 32);
config.security.admin_key[2].size = 32;
}
#endif
if (numAdminKeys > 0) {
LOG_INFO("Saving %d hard coded admin keys.", numAdminKeys);
config.security.admin_key_count = numAdminKeys;
saveToDisk(SEGMENT_CONFIG);
}
#endif
state = loadProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, sizeof(meshtastic_LocalModuleConfig),
&meshtastic_LocalModuleConfig_msg, &moduleConfig);
@ -1087,9 +1102,6 @@ void NodeDB::loadFromDisk()
bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct,
bool fullAtomic)
{
#ifdef ARCH_ESP32
concurrency::LockGuard g(spiLock);
#endif
bool okay = false;
#ifdef FSCom
auto f = SafeFile(filename, fullAtomic);
@ -1117,7 +1129,9 @@ bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_
bool NodeDB::saveChannelsToDisk()
{
#ifdef FSCom
spiLock->lock();
FSCom.mkdir("/prefs");
spiLock->unlock();
#endif
return saveProto(channelFileName, meshtastic_ChannelFile_size, &meshtastic_ChannelFile_msg, &channelFile);
}
@ -1125,12 +1139,15 @@ bool NodeDB::saveChannelsToDisk()
bool NodeDB::saveDeviceStateToDisk()
{
#ifdef FSCom
spiLock->lock();
FSCom.mkdir("/prefs");
spiLock->unlock();
#endif
// Note: if MAX_NUM_NODES=100 and meshtastic_NodeInfoLite_size=166, so will be approximately 17KB
// Because so huge we _must_ not use fullAtomic, because the filesystem is probably too small to hold two copies of this
return saveProto(prefFileName, sizeof(devicestate) + numMeshNodes * meshtastic_NodeInfoLite_size, &meshtastic_DeviceState_msg,
&devicestate, false);
size_t deviceStateSize;
pb_get_encoded_size(&deviceStateSize, meshtastic_DeviceState_fields, &devicestate);
return saveProto(prefFileName, deviceStateSize, &meshtastic_DeviceState_msg, &devicestate, false);
}
bool NodeDB::saveToDiskNoRetry(int saveWhat)
@ -1138,7 +1155,9 @@ bool NodeDB::saveToDiskNoRetry(int saveWhat)
bool success = true;
#ifdef FSCom
spiLock->lock();
FSCom.mkdir("/prefs");
spiLock->unlock();
#endif
if (saveWhat & SEGMENT_CONFIG) {
config.has_device = true;
@ -1189,7 +1208,9 @@ bool NodeDB::saveToDisk(int saveWhat)
if (!success) {
LOG_ERROR("Failed to save to disk, retrying");
#ifdef ARCH_NRF52 // @geeksville is not ready yet to say we should do this on other platforms. See bug #4184 discussion
spiLock->lock();
FSCom.format();
spiLock->unlock();
#endif
success = saveToDiskNoRetry(saveWhat);
@ -1508,4 +1529,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co
LOG_ERROR("A critical failure occurred, portduino is exiting");
exit(2);
#endif
}
}

View File

@ -145,7 +145,7 @@ class NodeDB
return &meshNodes->at(x);
}
meshtastic_NodeInfoLite *getMeshNode(NodeNum n);
virtual meshtastic_NodeInfoLite *getMeshNode(NodeNum n);
size_t getNumMeshNodes() { return numMeshNodes; }
// returns true if the maximum number of nodes is reached or we are running low on memory

View File

@ -4,7 +4,11 @@
#include <unordered_set>
/// We clear our old flood record 10 minutes after we see the last of it
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#define FLOOD_EXPIRE_TIME (5 * 1000L) // Don't allow too many packets to accumulate when fuzzing.
#else
#define FLOOD_EXPIRE_TIME (10 * 60 * 1000L)
#endif
/**
* A record of a recent message broadcast

View File

@ -12,6 +12,7 @@
#include "PhoneAPI.h"
#include "PowerFSM.h"
#include "RadioInterface.h"
#include "SPILock.h"
#include "TypeConversions.h"
#include "main.h"
#include "xmodem.h"
@ -54,7 +55,9 @@ void PhoneAPI::handleStartConfig()
// even if we were already connected - restart our state machine
state = STATE_SEND_MY_INFO;
pauseBluetoothLogging = true;
spiLock->lock();
filesManifest = getFiles("/", 10);
spiLock->unlock();
LOG_DEBUG("Got %d files in manifest", filesManifest.size());
LOG_INFO("Start API client config");

View File

@ -91,16 +91,16 @@ void RF95Interface::setTransmitEnable(bool txon)
#ifdef RF95_TXEN
digitalWrite(RF95_TXEN, txon ? 1 : 0);
#elif ARCH_PORTDUINO
if (settingsMap[txen] != RADIOLIB_NC) {
digitalWrite(settingsMap[txen], txon ? 1 : 0);
if (settingsMap[txen_pin] != RADIOLIB_NC) {
digitalWrite(settingsMap[txen_pin], txon ? 1 : 0);
}
#endif
#ifdef RF95_RXEN
digitalWrite(RF95_RXEN, txon ? 0 : 1);
#elif ARCH_PORTDUINO
if (settingsMap[rxen] != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen], txon ? 0 : 1);
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen_pin], txon ? 0 : 1);
}
#endif
}
@ -161,13 +161,13 @@ bool RF95Interface::init()
digitalWrite(RF95_RXEN, 1);
#endif
#if ARCH_PORTDUINO
if (settingsMap[txen] != RADIOLIB_NC) {
pinMode(settingsMap[txen], OUTPUT);
digitalWrite(settingsMap[txen], 0);
if (settingsMap[txen_pin] != RADIOLIB_NC) {
pinMode(settingsMap[txen_pin], OUTPUT);
digitalWrite(settingsMap[txen_pin], 0);
}
if (settingsMap[rxen] != RADIOLIB_NC) {
pinMode(settingsMap[rxen], OUTPUT);
digitalWrite(settingsMap[rxen], 0);
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
pinMode(settingsMap[rxen_pin], OUTPUT);
digitalWrite(settingsMap[rxen_pin], 0);
}
#endif
setTransmitEnable(false);
@ -331,4 +331,4 @@ bool RF95Interface::sleep()
return true;
}
#endif
#endif

View File

@ -297,7 +297,7 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
void printPacket(const char *prefix, const meshtastic_MeshPacket *p)
{
#ifdef DEBUG_PORT
#if defined(DEBUG_PORT) && !defined(DEBUG_MUTE)
std::string out = DEBUG_PORT.mt_sprintf("%s (id=0x%08x fr=0x%08x to=0x%08x, WantAck=%d, HopLim=%d Ch=0x%x", prefix, p->id,
p->from, p->to, p->want_ack, p->hop_limit, p->channel);
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
@ -637,4 +637,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p)
sendingPacket = p;
return p->encrypted.size + sizeof(PacketHeader);
}
}

View File

@ -271,6 +271,7 @@ void RadioLibInterface::onNotify(uint32_t notification)
uint32_t xmitMsec = getPacketTime(txp);
airTime->logAirtime(TX_LOG, xmitMsec);
}
LOG_DEBUG("%d packets remain in the TX queue", txQueue.getMaxLen() - txQueue.getFree());
}
}
}
@ -297,8 +298,8 @@ void RadioLibInterface::setTransmitDelay()
if (p->tx_after) {
unsigned long add_delay = p->rx_rssi ? getTxDelayMsecWeighted(p->rx_snr) : getTxDelayMsec();
unsigned long now = millis();
p->tx_after = max(p->tx_after + add_delay, now + add_delay);
notifyLater(now - p->tx_after, TRANSMIT_DELAY_COMPLETED, false);
p->tx_after = min(max(p->tx_after + add_delay, now + add_delay), now + 2 * getTxDelayMsecWeightedWorst(p->rx_snr));
notifyLater(p->tx_after - now, TRANSMIT_DELAY_COMPLETED, false);
} else if (p->rx_snr == 0 && p->rx_rssi == 0) {
/* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally.
* This assumption is valid because of the offset generated by the radio to account for the noise
@ -431,6 +432,7 @@ void RadioLibInterface::handleReceiveInterrupt()
// nodes.
meshtastic_MeshPacket *mp = packetPool.allocZeroed();
// Keep the assigned fields in sync with src/mqtt/MQTT.cpp:onReceiveProto
mp->from = radioBuffer.header.from;
mp->to = radioBuffer.header.to;
mp->id = radioBuffer.header.id;
@ -500,14 +502,13 @@ bool RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // Transmitter off now
startReceive(); // Restart receive mode (because startTransmit failed to put us in xmit mode)
} else {
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register
// bits
enableInterrupt(isrTxLevel0);
lastTxStart = millis();
printPacket("Started Tx", txp);
}
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register
// bits
enableInterrupt(isrTxLevel0);
return res == RADIOLIB_ERR_NONE;
}
}

View File

@ -187,7 +187,7 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
}
// don't override if a channel was requested and no need to set it when PKI is enforced
if (!p->channel && !p->pki_encrypted) {
if (!p->channel && !p->pki_encrypted && !isBroadcast(p->to)) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to);
if (node) {
p->channel = node->channel;
@ -679,4 +679,4 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
// cache/learn of the existence of nodes (i.e. FloodRouter) that they should not
handleReceived(p);
packetPool.release(p);
}
}

View File

@ -71,7 +71,7 @@ class Router : protected concurrency::OSThread
* RadioInterface calls this to queue up packets that have been received from the radio. The router is now responsible for
* freeing the packet
*/
void enqueueReceivedMessage(meshtastic_MeshPacket *p);
virtual void enqueueReceivedMessage(meshtastic_MeshPacket *p);
/**
* Send a packet on a suitable interface. This routine will

View File

@ -53,9 +53,9 @@ template <typename T> bool SX126xInterface<T>::init()
#if ARCH_PORTDUINO
float tcxoVoltage = (float)settingsMap[dio3_tcxo_voltage] / 1000;
if (settingsMap[sx126x_ant_sw] != RADIOLIB_NC) {
digitalWrite(settingsMap[sx126x_ant_sw], HIGH);
pinMode(settingsMap[sx126x_ant_sw], OUTPUT);
if (settingsMap[sx126x_ant_sw_pin] != RADIOLIB_NC) {
digitalWrite(settingsMap[sx126x_ant_sw_pin], HIGH);
pinMode(settingsMap[sx126x_ant_sw_pin], OUTPUT);
}
// FIXME: correct logic to default to not using TCXO if no voltage is specified for SX126X_DIO3_TCXO_VOLTAGE
#elif !defined(SX126X_DIO3_TCXO_VOLTAGE)
@ -123,8 +123,9 @@ template <typename T> bool SX126xInterface<T>::init()
// no effect
#if ARCH_PORTDUINO
if (res == RADIOLIB_ERR_NONE) {
LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen], settingsMap[txen]);
lora.setRfSwitchPins(settingsMap[rxen], settingsMap[txen]);
LOG_DEBUG("Use MCU pin %i as RXEN and pin %i as TXEN to control RF switching", settingsMap[rxen_pin],
settingsMap[txen_pin]);
lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]);
}
#else
#ifndef SX126X_RXEN
@ -343,4 +344,4 @@ template <typename T> bool SX126xInterface<T>::sleep()
return true;
}
#endif
#endif

View File

@ -35,13 +35,13 @@ template <typename T> bool SX128xInterface<T>::init()
enableFan();
#if ARCH_PORTDUINO
if (settingsMap[rxen] != RADIOLIB_NC) {
pinMode(settingsMap[rxen], OUTPUT);
digitalWrite(settingsMap[rxen], LOW); // Set low before becoming an output
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
pinMode(settingsMap[rxen_pin], OUTPUT);
digitalWrite(settingsMap[rxen_pin], LOW); // Set low before becoming an output
}
if (settingsMap[txen] != RADIOLIB_NC) {
pinMode(settingsMap[txen], OUTPUT);
digitalWrite(settingsMap[txen], LOW); // Set low before becoming an output
if (settingsMap[txen_pin] != RADIOLIB_NC) {
pinMode(settingsMap[txen_pin], OUTPUT);
digitalWrite(settingsMap[txen_pin], LOW); // Set low before becoming an output
}
#else
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // set not rx or tx mode
@ -90,8 +90,8 @@ template <typename T> bool SX128xInterface<T>::init()
lora.setRfSwitchPins(SX128X_RXEN, SX128X_TXEN);
}
#elif ARCH_PORTDUINO
if (res == RADIOLIB_ERR_NONE && settingsMap[rxen] != RADIOLIB_NC && settingsMap[txen] != RADIOLIB_NC) {
lora.setRfSwitchPins(settingsMap[rxen], settingsMap[txen]);
if (res == RADIOLIB_ERR_NONE && settingsMap[rxen_pin] != RADIOLIB_NC && settingsMap[txen_pin] != RADIOLIB_NC) {
lora.setRfSwitchPins(settingsMap[rxen_pin], settingsMap[txen_pin]);
}
#endif
@ -171,11 +171,11 @@ template <typename T> void SX128xInterface<T>::setStandby()
LOG_ERROR("SX128x standby %s%d", radioLibErr, err);
assert(err == RADIOLIB_ERR_NONE);
#if ARCH_PORTDUINO
if (settingsMap[rxen] != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen], LOW);
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen_pin], LOW);
}
if (settingsMap[txen] != RADIOLIB_NC) {
digitalWrite(settingsMap[txen], LOW);
if (settingsMap[txen_pin] != RADIOLIB_NC) {
digitalWrite(settingsMap[txen_pin], LOW);
}
#else
#if defined(SX128X_RXEN) && (SX128X_RXEN != RADIOLIB_NC) // we have RXEN/TXEN control - turn off RX and TX power
@ -207,11 +207,11 @@ template <typename T> void SX128xInterface<T>::addReceiveMetadata(meshtastic_Mes
template <typename T> void SX128xInterface<T>::configHardwareForSend()
{
#if ARCH_PORTDUINO
if (settingsMap[txen] != RADIOLIB_NC) {
digitalWrite(settingsMap[txen], HIGH);
if (settingsMap[txen_pin] != RADIOLIB_NC) {
digitalWrite(settingsMap[txen_pin], HIGH);
}
if (settingsMap[rxen] != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen], LOW);
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen_pin], LOW);
}
#else
@ -238,11 +238,11 @@ template <typename T> void SX128xInterface<T>::startReceive()
setStandby();
#if ARCH_PORTDUINO
if (settingsMap[rxen] != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen], HIGH);
if (settingsMap[rxen_pin] != RADIOLIB_NC) {
digitalWrite(settingsMap[rxen_pin], HIGH);
}
if (settingsMap[txen] != RADIOLIB_NC) {
digitalWrite(settingsMap[txen], LOW);
if (settingsMap[txen_pin] != RADIOLIB_NC) {
digitalWrite(settingsMap[txen_pin], LOW);
}
#else
@ -312,4 +312,4 @@ template <typename T> bool SX128xInterface<T>::sleep()
return true;
}
#endif
#endif

View File

@ -74,11 +74,17 @@ template <class T> class TypedQueue
{
std::queue<T> q;
concurrency::OSThread *reader = NULL;
int maxElements;
public:
explicit TypedQueue(int maxElements) {}
explicit TypedQueue(int _maxElements) : maxElements(_maxElements) {}
int numFree() { return 1; } // Always claim 1 free, because we can grow to any size
int numFree()
{
if (maxElements <= 0)
return 1; // Always claim 1 free, because we can grow to any size
return maxElements - numUsed();
}
bool isEmpty() { return q.empty(); }
@ -86,6 +92,9 @@ template <class T> class TypedQueue
bool enqueue(T x, TickType_t maxWait = portMAX_DELAY)
{
if (numFree() <= 0)
return false;
if (reader) {
reader->setInterval(0);
concurrency::mainDelay.interrupt();
@ -112,4 +121,4 @@ template <class T> class TypedQueue
void setReader(concurrency::OSThread *t) { reader = t; }
};
#endif
#endif

View File

@ -66,8 +66,9 @@ static int32_t reconnectETH()
syslog.enable();
}
// initWebServer();
#if !MESHTASTIC_EXCLUDE_SOCKETAPI
initApiServer();
#endif
ethStartupComplete = true;
}
@ -107,6 +108,11 @@ static int32_t reconnectETH()
bool initEthernet()
{
if (config.network.eth_enabled) {
#ifdef PIN_ETH_POWER_EN
pinMode(PIN_ETH_POWER_EN, OUTPUT);
digitalWrite(PIN_ETH_POWER_EN, HIGH); // Power up.
delay(100);
#endif
#ifdef PIN_ETHERNET_RESET
pinMode(PIN_ETHERNET_RESET, OUTPUT);
@ -115,6 +121,12 @@ bool initEthernet()
digitalWrite(PIN_ETHERNET_RESET, HIGH); // Reset Time.
#endif
#ifdef RAK11310 // Initialize the SPI port
ETH_SPI_PORT.setSCK(PIN_SPI0_SCK);
ETH_SPI_PORT.setTX(PIN_SPI0_MOSI);
ETH_SPI_PORT.setRX(PIN_SPI0_MISO);
ETH_SPI_PORT.begin();
#endif
Ethernet.init(ETH_SPI_PORT, PIN_ETHERNET_SS);
uint8_t mac[6];

View File

@ -63,6 +63,8 @@ PB_BIND(meshtastic_Config_SessionkeyConfig, meshtastic_Config_SessionkeyConfig,

View File

@ -139,6 +139,14 @@ typedef enum _meshtastic_Config_NetworkConfig_AddressMode {
meshtastic_Config_NetworkConfig_AddressMode_STATIC = 1
} meshtastic_Config_NetworkConfig_AddressMode;
/* Available flags auxiliary network protocols */
typedef enum _meshtastic_Config_NetworkConfig_ProtocolFlags {
/* Do not broadcast packets over any network protocol */
meshtastic_Config_NetworkConfig_ProtocolFlags_NO_BROADCAST = 0,
/* Enable broadcasting packets via UDP over the local network */
meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST = 1
} meshtastic_Config_NetworkConfig_ProtocolFlags;
/* How the GPS coordinates are displayed on the OLED screen. */
typedef enum _meshtastic_Config_DisplayConfig_GpsCoordinateFormat {
/* GPS coordinates are displayed in the normal decimal degrees format:
@ -429,6 +437,8 @@ typedef struct _meshtastic_Config_NetworkConfig {
meshtastic_Config_NetworkConfig_IpV4Config ipv4_config;
/* rsyslog Server and Port */
char rsyslog_server[33];
/* Flags for enabling/disabling network protocols */
uint32_t enabled_protocols;
} meshtastic_Config_NetworkConfig;
/* Display Config */
@ -613,6 +623,10 @@ extern "C" {
#define _meshtastic_Config_NetworkConfig_AddressMode_MAX meshtastic_Config_NetworkConfig_AddressMode_STATIC
#define _meshtastic_Config_NetworkConfig_AddressMode_ARRAYSIZE ((meshtastic_Config_NetworkConfig_AddressMode)(meshtastic_Config_NetworkConfig_AddressMode_STATIC+1))
#define _meshtastic_Config_NetworkConfig_ProtocolFlags_MIN meshtastic_Config_NetworkConfig_ProtocolFlags_NO_BROADCAST
#define _meshtastic_Config_NetworkConfig_ProtocolFlags_MAX meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST
#define _meshtastic_Config_NetworkConfig_ProtocolFlags_ARRAYSIZE ((meshtastic_Config_NetworkConfig_ProtocolFlags)(meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST+1))
#define _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN meshtastic_Config_DisplayConfig_GpsCoordinateFormat_DEC
#define _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MAX meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OSGR
#define _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_ARRAYSIZE ((meshtastic_Config_DisplayConfig_GpsCoordinateFormat)(meshtastic_Config_DisplayConfig_GpsCoordinateFormat_OSGR+1))
@ -674,7 +688,7 @@ extern "C" {
#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
#define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN}
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""}
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, "", 0}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0}
@ -685,7 +699,7 @@ extern "C" {
#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
#define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN}
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""}
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, "", 0}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0}
@ -739,6 +753,7 @@ extern "C" {
#define meshtastic_Config_NetworkConfig_address_mode_tag 7
#define meshtastic_Config_NetworkConfig_ipv4_config_tag 8
#define meshtastic_Config_NetworkConfig_rsyslog_server_tag 9
#define meshtastic_Config_NetworkConfig_enabled_protocols_tag 10
#define meshtastic_Config_DisplayConfig_screen_on_secs_tag 1
#define meshtastic_Config_DisplayConfig_gps_format_tag 2
#define meshtastic_Config_DisplayConfig_auto_screen_carousel_secs_tag 3
@ -867,7 +882,8 @@ X(a, STATIC, SINGULAR, STRING, ntp_server, 5) \
X(a, STATIC, SINGULAR, BOOL, eth_enabled, 6) \
X(a, STATIC, SINGULAR, UENUM, address_mode, 7) \
X(a, STATIC, OPTIONAL, MESSAGE, ipv4_config, 8) \
X(a, STATIC, SINGULAR, STRING, rsyslog_server, 9)
X(a, STATIC, SINGULAR, STRING, rsyslog_server, 9) \
X(a, STATIC, SINGULAR, UINT32, enabled_protocols, 10)
#define meshtastic_Config_NetworkConfig_CALLBACK NULL
#define meshtastic_Config_NetworkConfig_DEFAULT NULL
#define meshtastic_Config_NetworkConfig_ipv4_config_MSGTYPE meshtastic_Config_NetworkConfig_IpV4Config
@ -972,12 +988,12 @@ extern const pb_msgdesc_t meshtastic_Config_SessionkeyConfig_msg;
#define meshtastic_Config_DisplayConfig_size 30
#define meshtastic_Config_LoRaConfig_size 85
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
#define meshtastic_Config_NetworkConfig_size 196
#define meshtastic_Config_NetworkConfig_size 202
#define meshtastic_Config_PositionConfig_size 62
#define meshtastic_Config_PowerConfig_size 52
#define meshtastic_Config_SecurityConfig_size 178
#define meshtastic_Config_SessionkeyConfig_size 0
#define meshtastic_Config_size 199
#define meshtastic_Config_size 205
#ifdef __cplusplus
} /* extern "C" */

View File

@ -51,6 +51,8 @@ typedef enum _meshtastic_Language {
meshtastic_Language_GREEK = 13,
/* Norwegian */
meshtastic_Language_NORWEGIAN = 14,
/* Slovenian */
meshtastic_Language_SLOVENIAN = 15,
/* Simplified Chinese (experimental) */
meshtastic_Language_SIMPLIFIED_CHINESE = 30,
/* Traditional Chinese (experimental) */
@ -71,6 +73,8 @@ typedef struct _meshtastic_NodeFilter {
bool position_switch;
/* Filter nodes by matching name string */
char node_name[16];
/* Filter based on channel */
int8_t channel;
} meshtastic_NodeFilter;
typedef struct _meshtastic_NodeHighlight {
@ -138,10 +142,10 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_DeviceUIConfig_init_default {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_default, false, meshtastic_NodeHighlight_init_default, {0, {0}}}
#define meshtastic_NodeFilter_init_default {0, 0, 0, 0, 0, ""}
#define meshtastic_NodeFilter_init_default {0, 0, 0, 0, 0, "", 0}
#define meshtastic_NodeHighlight_init_default {0, 0, 0, 0, ""}
#define meshtastic_DeviceUIConfig_init_zero {0, 0, 0, 0, 0, 0, _meshtastic_Theme_MIN, 0, 0, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_zero, false, meshtastic_NodeHighlight_init_zero, {0, {0}}}
#define meshtastic_NodeFilter_init_zero {0, 0, 0, 0, 0, ""}
#define meshtastic_NodeFilter_init_zero {0, 0, 0, 0, 0, "", 0}
#define meshtastic_NodeHighlight_init_zero {0, 0, 0, 0, ""}
/* Field tags (for use in manual encoding/decoding) */
@ -151,6 +155,7 @@ extern "C" {
#define meshtastic_NodeFilter_hops_away_tag 4
#define meshtastic_NodeFilter_position_switch_tag 5
#define meshtastic_NodeFilter_node_name_tag 6
#define meshtastic_NodeFilter_channel_tag 7
#define meshtastic_NodeHighlight_chat_switch_tag 1
#define meshtastic_NodeHighlight_position_switch_tag 2
#define meshtastic_NodeHighlight_telemetry_switch_tag 3
@ -198,7 +203,8 @@ X(a, STATIC, SINGULAR, BOOL, offline_switch, 2) \
X(a, STATIC, SINGULAR, BOOL, public_key_switch, 3) \
X(a, STATIC, SINGULAR, INT32, hops_away, 4) \
X(a, STATIC, SINGULAR, BOOL, position_switch, 5) \
X(a, STATIC, SINGULAR, STRING, node_name, 6)
X(a, STATIC, SINGULAR, STRING, node_name, 6) \
X(a, STATIC, SINGULAR, INT32, channel, 7)
#define meshtastic_NodeFilter_CALLBACK NULL
#define meshtastic_NodeFilter_DEFAULT NULL
@ -222,8 +228,8 @@ extern const pb_msgdesc_t meshtastic_NodeHighlight_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_DEVICE_UI_PB_H_MAX_SIZE meshtastic_DeviceUIConfig_size
#define meshtastic_DeviceUIConfig_size 117
#define meshtastic_NodeFilter_size 36
#define meshtastic_DeviceUIConfig_size 128
#define meshtastic_NodeFilter_size 47
#define meshtastic_NodeHighlight_size 25
#ifdef __cplusplus

View File

@ -187,7 +187,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalConfig_size
#define meshtastic_LocalConfig_size 735
#define meshtastic_LocalConfig_size 741
#define meshtastic_LocalModuleConfig_size 699
#ifdef __cplusplus

View File

@ -81,7 +81,9 @@ typedef enum _meshtastic_TelemetrySensorType {
/* ClimateGuard RadSens, radiation, Geiger-Muller Tube */
meshtastic_TelemetrySensorType_RADSENS = 33,
/* High accuracy current and voltage */
meshtastic_TelemetrySensorType_INA226 = 34
meshtastic_TelemetrySensorType_INA226 = 34,
/* DFRobot Gravity tipping bucket rain gauge */
meshtastic_TelemetrySensorType_DFROBOT_RAIN = 35
} meshtastic_TelemetrySensorType;
/* Struct definitions */
@ -162,6 +164,12 @@ typedef struct _meshtastic_EnvironmentMetrics {
/* Radiation in µR/h */
bool has_radiation;
float radiation;
/* Rainfall in the last hour in mm */
bool has_rainfall_1h;
float rainfall_1h;
/* Rainfall in the last 24 hours in mm */
bool has_rainfall_24h;
float rainfall_24h;
} meshtastic_EnvironmentMetrics;
/* Power Metrics (voltage / current / etc) */
@ -306,8 +314,8 @@ extern "C" {
/* Helper constants for enums */
#define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET
#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_INA226
#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_INA226+1))
#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_DFROBOT_RAIN
#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_DFROBOT_RAIN+1))
@ -320,7 +328,7 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_DeviceMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
@ -328,7 +336,7 @@ extern "C" {
#define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}}
#define meshtastic_Nau7802Config_init_default {0, 0}
#define meshtastic_DeviceMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
@ -360,6 +368,8 @@ extern "C" {
#define meshtastic_EnvironmentMetrics_wind_gust_tag 16
#define meshtastic_EnvironmentMetrics_wind_lull_tag 17
#define meshtastic_EnvironmentMetrics_radiation_tag 18
#define meshtastic_EnvironmentMetrics_rainfall_1h_tag 19
#define meshtastic_EnvironmentMetrics_rainfall_24h_tag 20
#define meshtastic_PowerMetrics_ch1_voltage_tag 1
#define meshtastic_PowerMetrics_ch1_current_tag 2
#define meshtastic_PowerMetrics_ch2_voltage_tag 3
@ -431,7 +441,9 @@ X(a, STATIC, OPTIONAL, FLOAT, wind_speed, 14) \
X(a, STATIC, OPTIONAL, FLOAT, weight, 15) \
X(a, STATIC, OPTIONAL, FLOAT, wind_gust, 16) \
X(a, STATIC, OPTIONAL, FLOAT, wind_lull, 17) \
X(a, STATIC, OPTIONAL, FLOAT, radiation, 18)
X(a, STATIC, OPTIONAL, FLOAT, radiation, 18) \
X(a, STATIC, OPTIONAL, FLOAT, rainfall_1h, 19) \
X(a, STATIC, OPTIONAL, FLOAT, rainfall_24h, 20)
#define meshtastic_EnvironmentMetrics_CALLBACK NULL
#define meshtastic_EnvironmentMetrics_DEFAULT NULL
@ -530,12 +542,12 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
#define MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_MAX_SIZE meshtastic_Telemetry_size
#define meshtastic_AirQualityMetrics_size 78
#define meshtastic_DeviceMetrics_size 27
#define meshtastic_EnvironmentMetrics_size 91
#define meshtastic_EnvironmentMetrics_size 103
#define meshtastic_HealthMetrics_size 11
#define meshtastic_LocalStats_size 60
#define meshtastic_Nau7802Config_size 16
#define meshtastic_PowerMetrics_size 30
#define meshtastic_Telemetry_size 98
#define meshtastic_Telemetry_size 110
#ifdef __cplusplus
} /* extern "C" */

View File

@ -10,6 +10,7 @@
#include "mesh/wifi/WiFiAPClient.h"
#endif
#include "Led.h"
#include "SPILock.h"
#include "power.h"
#include "serialization/JSON.h"
#include <FSCommon.h>
@ -236,6 +237,7 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
void htmlDeleteDir(const char *dirname)
{
File root = FSCom.open(dirname);
if (!root) {
return;
@ -318,6 +320,7 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
concurrency::LockGuard g(spiLock);
auto fileList = htmlListDir("/static", 10);
// create json output structure
@ -349,9 +352,12 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "DELETE");
if (params->getQueryParameter("delete", paramValDelete)) {
std::string pathDelete = "/" + paramValDelete;
concurrency::LockGuard g(spiLock);
if (FSCom.remove(pathDelete.c_str())) {
LOG_INFO("%s", pathDelete.c_str());
JSONObject jsonObjOuter;
jsonObjOuter["status"] = new JSONValue("ok");
@ -360,6 +366,7 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
delete value;
return;
} else {
LOG_INFO("%s", pathDelete.c_str());
JSONObject jsonObjOuter;
jsonObjOuter["status"] = new JSONValue("Error");
@ -393,6 +400,8 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
filenameGzip = "/static/index.html.gz";
}
concurrency::LockGuard g(spiLock);
if (FSCom.exists(filename.c_str())) {
file = FSCom.open(filename.c_str());
if (!file.available()) {
@ -410,6 +419,7 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
file = FSCom.open(filenameGzip.c_str());
res->setHeader("Content-Type", "text/html");
if (!file.available()) {
LOG_WARN("File not available - %s", filenameGzip.c_str());
res->println("Web server is running.<br><br>The content you are looking for can't be found. Please see: <a "
"href=https://meshtastic.org/docs/software/web-client/>FAQ</a>.<br><br><a "
@ -535,6 +545,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
// concepts of the body parser functionality easier to understand.
std::string pathname = "/static/" + filename;
concurrency::LockGuard g(spiLock);
// Create a new file to stream the data into
File file = FSCom.open(pathname.c_str(), FILE_O_WRITE);
size_t fileLength = 0;
@ -571,6 +582,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
file.flush();
file.close();
res->printf("<p>Saved %d bytes to %s</p>", (int)fileLength, pathname.c_str());
}
if (!didwrite) {
@ -642,9 +654,11 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
jsonObjMemory["heap_free"] = new JSONValue((int)memGet.getFreeHeap());
jsonObjMemory["psram_total"] = new JSONValue((int)memGet.getPsramSize());
jsonObjMemory["psram_free"] = new JSONValue((int)memGet.getFreePsram());
spiLock->lock();
jsonObjMemory["fs_total"] = new JSONValue((int)FSCom.totalBytes());
jsonObjMemory["fs_used"] = new JSONValue((int)FSCom.usedBytes());
jsonObjMemory["fs_free"] = new JSONValue(int(FSCom.totalBytes() - FSCom.usedBytes()));
spiLock->unlock();
// data->power
JSONObject jsonObjPower;
@ -786,6 +800,7 @@ void handleDeleteFsContent(HTTPRequest *req, HTTPResponse *res)
LOG_INFO("Delete files from /static/* : ");
concurrency::LockGuard g(spiLock);
htmlDeleteDir("/static");
res->println("<p><hr><p><a href=/admin>Back to admin</a>");

View File

@ -1,6 +1,7 @@
#include "configuration.h"
#include "FSCommon.h"
#include "SPILock.h"
#include "mesh-pb-constants.h"
#include <Arduino.h>
#include <pb_decode.h>
@ -55,9 +56,12 @@ bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count)
/// Write to an arduino file
bool writecb(pb_ostream_t *stream, const uint8_t *buf, size_t count)
{
spiLock->lock();
auto file = (Print *)stream->state;
// LOG_DEBUG("writing %d bytes to protobuf file", count);
return file->write(buf, count) == count;
bool status = file->write(buf, count) == count;
spiLock->unlock();
return status;
}
#endif
@ -68,4 +72,4 @@ bool is_in_helper(uint32_t n, const uint32_t *array, pb_size_t count)
return true;
return false;
}
}

View File

@ -82,8 +82,6 @@ char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"
volatile bool isWebServerReady;
volatile bool isCertReady;
HttpAPI webAPI;
PiWebServerThread *piwebServerThread;
/**
@ -247,7 +245,7 @@ int handleAPIv1ToRadio(const struct _u_request *req, struct _u_response *res, vo
portduinoVFS->mountpoint(configWeb.rootPath);
LOG_DEBUG("Received %d bytes from PUT request", s);
webAPI.handleToRadio(buffer, s);
static_cast<HttpAPI *>(user_data)->handleToRadio(buffer, s);
LOG_DEBUG("end web->radio ");
return U_CALLBACK_COMPLETE;
}
@ -279,7 +277,7 @@ int handleAPIv1FromRadio(const struct _u_request *req, struct _u_response *res,
if (valueAll == "true") {
while (len) {
len = webAPI.getFromRadio(txBuf);
len = static_cast<HttpAPI *>(user_data)->getFromRadio(txBuf);
ulfius_set_response_properties(res, U_OPT_STATUS, 200, U_OPT_BINARY_BODY, txBuf, len);
const char *tmpa = (const char *)txBuf;
ulfius_set_string_body_response(res, 200, tmpa);
@ -289,7 +287,7 @@ int handleAPIv1FromRadio(const struct _u_request *req, struct _u_response *res,
}
// Otherwise, just return one protobuf
} else {
len = webAPI.getFromRadio(txBuf);
len = static_cast<HttpAPI *>(user_data)->getFromRadio(txBuf);
const char *tmpa = (const char *)txBuf;
ulfius_set_binary_body_response(res, 200, tmpa, len);
// LOG_DEBUG("\n----webAPI response:");
@ -497,10 +495,10 @@ PiWebServerThread::PiWebServerThread()
u_map_put(instanceWeb.default_headers, "Access-Control-Allow-Origin", "*");
// Maximum body size sent by the client is 1 Kb
instanceWeb.max_post_body_size = 1024;
ulfius_add_endpoint_by_val(&instanceWeb, "GET", PREFIX, "/api/v1/fromradio/*", 1, &handleAPIv1FromRadio, NULL);
ulfius_add_endpoint_by_val(&instanceWeb, "OPTIONS", PREFIX, "/api/v1/fromradio/*", 1, &handleAPIv1FromRadio, NULL);
ulfius_add_endpoint_by_val(&instanceWeb, "PUT", PREFIX, "/api/v1/toradio/*", 1, &handleAPIv1ToRadio, configWeb.rootPath);
ulfius_add_endpoint_by_val(&instanceWeb, "OPTIONS", PREFIX, "/api/v1/toradio/*", 1, &handleAPIv1ToRadio, NULL);
ulfius_add_endpoint_by_val(&instanceWeb, "GET", PREFIX, "/api/v1/fromradio/*", 1, &handleAPIv1FromRadio, &webAPI);
ulfius_add_endpoint_by_val(&instanceWeb, "OPTIONS", PREFIX, "/api/v1/fromradio/*", 1, &handleAPIv1FromRadio, &webAPI);
ulfius_add_endpoint_by_val(&instanceWeb, "PUT", PREFIX, "/api/v1/toradio/*", 1, &handleAPIv1ToRadio, &webAPI);
ulfius_add_endpoint_by_val(&instanceWeb, "OPTIONS", PREFIX, "/api/v1/toradio/*", 1, &handleAPIv1ToRadio, &webAPI);
// Add callback function to all endpoints for the Web Server
ulfius_add_endpoint_by_val(&instanceWeb, "GET", NULL, "/*", 2, &callback_static_file, &configWeb);
@ -525,13 +523,12 @@ PiWebServerThread::~PiWebServerThread()
u_map_clean(&configWeb.mime_types);
ulfius_stop_framework(&instanceWeb);
ulfius_stop_framework(&instanceWeb);
ulfius_clean_instance(&instanceWeb);
free(configWeb.rootPath);
ulfius_clean_instance(&instanceService);
ulfius_clean_instance(&instanceService);
free(key_pem);
free(cert_pem);
LOG_INFO("End framework");
}
#endif
#endif
#endif

View File

@ -23,24 +23,6 @@ struct _file_config {
char *rootPath;
};
class PiWebServerThread
{
private:
char *key_pem = NULL;
char *cert_pem = NULL;
// struct _u_map mime_types;
std::string webrootpath;
public:
PiWebServerThread();
~PiWebServerThread();
int CreateSSLCertificate();
int CheckSSLandLoad();
uint32_t requestRestart = 0;
struct _u_instance instanceWeb;
struct _u_instance instanceService;
};
class HttpAPI : public PhoneAPI
{
@ -55,6 +37,24 @@ class HttpAPI : public PhoneAPI
virtual bool checkIsConnected() override { return true; } // FIXME, be smarter about this
};
class PiWebServerThread
{
private:
char *key_pem = NULL;
char *cert_pem = NULL;
// struct _u_map mime_types;
std::string webrootpath;
HttpAPI webAPI;
public:
PiWebServerThread();
~PiWebServerThread();
int CreateSSLCertificate();
int CheckSSLandLoad();
uint32_t requestRestart = 0;
struct _u_instance instanceWeb;
};
extern PiWebServerThread *piwebServerThread;
#endif

View File

@ -106,7 +106,9 @@ static void onNetworkConnected()
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER
initWebServer();
#endif
#if !MESHTASTIC_EXCLUDE_SOCKETAPI
initApiServer();
#endif
APStartupComplete = true;
}
@ -141,6 +143,11 @@ static int32_t reconnectWiFi()
delay(5000);
if (!WiFi.isConnected()) {
#ifdef CONFIG_IDF_TARGET_ESP32C3
WiFi.mode(WIFI_MODE_NULL);
WiFi.useStaticBuffers(true);
WiFi.mode(WIFI_STA);
#endif
WiFi.begin(wifiName, wifiPsw);
}
isReconnecting = false;

Some files were not shown because too many files have changed in this diff Show More