mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-04 03:18:10 +10:00
Compare commits
18
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07741e669f | ||
|
|
195f42af82 | ||
|
|
089af764ec | ||
|
|
7be5426f34 | ||
|
|
9ec63b5eb2 | ||
|
|
22a9346fe0 | ||
|
|
c0e52e6e1c | ||
|
|
11df30a85f | ||
|
|
8d8ff21e7c | ||
|
|
9c72767c01 | ||
|
|
c0425d7444 | ||
|
|
d7db0f5829 | ||
|
|
048e5187ba | ||
|
|
4234fe6f86 | ||
|
|
126861fd16 | ||
|
|
47a6c4c6a0 | ||
|
|
87f1f9d349 | ||
|
|
4ccdd80090 |
@@ -0,0 +1,51 @@
|
||||
name: Build MacOS Binary
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
macos_ver:
|
||||
required: false
|
||||
default: "26" # ARM64
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-MacOS:
|
||||
runs-on: macos-${{ inputs.macos_ver }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install deps
|
||||
shell: bash
|
||||
run: |
|
||||
brew update
|
||||
brew install platformio yaml-cpp libuv openssl@3 libusb argp-standalone pkg-config
|
||||
|
||||
- name: Get release version string
|
||||
run: |
|
||||
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
|
||||
id: version
|
||||
|
||||
- name: Build for MacOS
|
||||
run: |
|
||||
platformio run -e native-macos
|
||||
env:
|
||||
PKG_VERSION: ${{ steps.version.outputs.long }}
|
||||
# Errors in this step should not fail the entire workflow while MacOS support is in development.
|
||||
continue-on-error: true
|
||||
|
||||
- name: List output files
|
||||
run: ls -lah .pio/build/native-macos/
|
||||
|
||||
- name: Store binaries as an artifact
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: firmware-macos-${{ inputs.macos_ver }}-${{ steps.version.outputs.long }}
|
||||
overwrite: true
|
||||
path: |
|
||||
.pio/build/native-macos/meshtasticd
|
||||
@@ -116,6 +116,20 @@ jobs:
|
||||
build_location: local
|
||||
secrets: inherit
|
||||
|
||||
MacOS:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
macos_ver:
|
||||
- "26" # ARM64
|
||||
# - '26-intel' # x86_64
|
||||
- "15" # ARM64
|
||||
# - '15-intel' # x86_64
|
||||
uses: ./.github/workflows/build_macos_bin.yml
|
||||
with:
|
||||
macos_ver: ${{ matrix.macos_ver }}
|
||||
# secrets: inherit
|
||||
|
||||
package-pio-deps-native-tft:
|
||||
if: ${{ github.repository == 'meshtastic/firmware' && github.event_name == 'workflow_dispatch' }}
|
||||
uses: ./.github/workflows/package_pio_deps.yml
|
||||
@@ -286,6 +300,7 @@ jobs:
|
||||
- gather-artifacts
|
||||
- build-debian-src
|
||||
- package-pio-deps-native-tft
|
||||
# - MacOS
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
+3
-3
@@ -8,15 +8,15 @@ plugins:
|
||||
uri: https://github.com/trunk-io/plugins
|
||||
lint:
|
||||
enabled:
|
||||
- checkov@3.2.524
|
||||
- renovate@43.141.0
|
||||
- checkov@3.2.525
|
||||
- renovate@43.150.0
|
||||
- prettier@3.8.3
|
||||
- trufflehog@3.95.2
|
||||
- yamllint@1.38.0
|
||||
- bandit@1.9.4
|
||||
- trivy@0.70.0
|
||||
- taplo@0.10.0
|
||||
- ruff@0.15.11
|
||||
- ruff@0.15.12
|
||||
- isort@8.0.1
|
||||
- markdownlint@0.48.0
|
||||
- oxipng@10.1.1
|
||||
|
||||
+2
-1
@@ -31,5 +31,6 @@ basename=meshtasticd-$1-$VERSION
|
||||
pio pkg install --environment "$PIO_ENV" || platformioFailed
|
||||
pio run --environment "$PIO_ENV" || platformioFailed
|
||||
|
||||
cp "$BUILDDIR/meshtasticd" "$OUTDIR/meshtasticd_linux_$(uname -m)"
|
||||
os_name=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
cp "$BUILDDIR/meshtasticd" "$OUTDIR/meshtasticd_${os_name}_$(uname -m)"
|
||||
cp bin/native-install.* $OUTDIR/
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "esp32s3_out.ld",
|
||||
"partitions": "default_16MB.csv",
|
||||
"memory_type": "qio_opi"
|
||||
},
|
||||
"core": "esp32",
|
||||
"extra_flags": [
|
||||
"-DBOARD_HAS_PSRAM",
|
||||
"-DARDUINO_USB_CDC_ON_BOOT=1",
|
||||
"-DARDUINO_USB_MODE=1",
|
||||
"-DARDUINO_RUNNING_CORE=1",
|
||||
"-DARDUINO_EVENT_RUNNING_CORE=1"
|
||||
],
|
||||
"f_cpu": "240000000L",
|
||||
"f_flash": "80000000L",
|
||||
"flash_mode": "qio",
|
||||
"psram_type": "opi",
|
||||
"hwids": [["0x303A", "0x1001"]],
|
||||
"mcu": "esp32s3",
|
||||
"variant": "heltec_v4_r8"
|
||||
},
|
||||
"connectivity": ["wifi", "bluetooth", "lora"],
|
||||
"debug": {
|
||||
"default_tool": "esp-builtin",
|
||||
"onboard_tools": ["esp-builtin"],
|
||||
"openocd_target": "esp32s3.cfg"
|
||||
},
|
||||
"frameworks": ["arduino", "espidf"],
|
||||
"name": "heltec_wifi_lora_32 v4 r8 (16 MB FLASH, 8 MB PSRAM)",
|
||||
"upload": {
|
||||
"flash_size": "16MB",
|
||||
"maximum_ram_size": 327680,
|
||||
"maximum_size": 16777216,
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true,
|
||||
"require_upload_port": true,
|
||||
"speed": 921600
|
||||
},
|
||||
"url": "https://heltec.org/",
|
||||
"vendor": "heltec"
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 188 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 188 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 278 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 456 KiB |
Vendored
+1
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/bash
|
||||
set -e
|
||||
export DEBEMAIL="jbennett@incomsystems.biz"
|
||||
export PLATFORMIO_LIBDEPS_DIR=pio/libdeps
|
||||
export PLATFORMIO_PACKAGES_DIR=pio/packages
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ lib_deps =
|
||||
[device-ui_base]
|
||||
lib_deps =
|
||||
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
|
||||
https://github.com/meshtastic/device-ui/archive/56e1da4e7d30abcd746a2092a30e422f8cf5fc2b.zip
|
||||
https://github.com/meshtastic/device-ui/archive/1ddcc9da2e60c013d6fc515fb73fb63fac75f9fd.zip
|
||||
|
||||
; Common libs for environmental measurements in telemetry module
|
||||
[environmental_base]
|
||||
|
||||
@@ -755,8 +755,10 @@ void Power::reboot()
|
||||
rp2040.reboot();
|
||||
#elif defined(ARCH_PORTDUINO)
|
||||
deInitApiServer();
|
||||
#ifdef __linux__
|
||||
if (aLinuxInputImpl)
|
||||
aLinuxInputImpl->deInit();
|
||||
#endif
|
||||
SPI.end();
|
||||
Wire.end();
|
||||
Serial1.end();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../freertosinc.h"
|
||||
#include "Print.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||
#include <Print.h>
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -157,6 +157,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#elif defined(HELTEC_MESH_NODE_T096)
|
||||
#define NUM_PA_POINTS 22
|
||||
#define TX_GAIN_LORA 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 12, 11, 10, 9, 8, 7
|
||||
#elif defined(HELTEC_V4_R8)
|
||||
#define NUM_PA_POINTS 22
|
||||
#define TX_GAIN_LORA 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 11, 11, 10, 9, 8, 7
|
||||
#else
|
||||
// If a board enables USE_KCT8103L_PA but does not match a known variant and has
|
||||
// not already provided a PA curve, fail at compile time to avoid unsafe defaults.
|
||||
|
||||
@@ -70,20 +70,25 @@ bool GPSUpdateScheduling::isUpdateDue()
|
||||
// Have we been searching for a GPS position for too long?
|
||||
bool GPSUpdateScheduling::searchedTooLong()
|
||||
{
|
||||
constexpr uint32_t oneMinuteMs = 60UL * 1000UL;
|
||||
constexpr uint32_t maxSearchClampMs = 15UL * oneMinuteMs; // Hard cap: 15 minutes is always too long
|
||||
uint32_t elapsed = elapsedSearchMs();
|
||||
|
||||
// Anything over 15 minutes is too long, regardless of the broadcast interval.
|
||||
// TODO: Make a smarter algorithm that backs off the search dwell time when not getting a lock.
|
||||
if (elapsed > maxSearchClampMs)
|
||||
return true;
|
||||
|
||||
uint32_t minimumOrConfiguredSecs =
|
||||
Default::getConfiguredOrMinimumValue(config.position.position_broadcast_secs, default_broadcast_interval_secs);
|
||||
uint32_t maxSearchMs = Default::getConfiguredOrDefaultMs(minimumOrConfiguredSecs, default_broadcast_interval_secs);
|
||||
// If broadcast interval set to max, no such thing as "too long"
|
||||
if (maxSearchMs == UINT32_MAX)
|
||||
return false;
|
||||
|
||||
// If we've been searching longer than our position broadcast interval: that's too long
|
||||
else if (elapsedSearchMs() > maxSearchMs)
|
||||
if (elapsed > maxSearchMs)
|
||||
return true;
|
||||
|
||||
// Otherwise, not too long yet!
|
||||
else
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Updates the predicted time-to-get-lock, by exponentially smoothing the latest observation
|
||||
|
||||
@@ -428,7 +428,7 @@ static LGFX *tft = nullptr;
|
||||
|
||||
#elif defined(ST7789_CS)
|
||||
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
|
||||
#ifdef HELTEC_V4_TFT
|
||||
#if defined(HELTEC_V4_TFT) || defined(HELTEC_V4_R8_TFT)
|
||||
#include "chsc6x.h"
|
||||
#include "lgfx/v1/Touch.hpp"
|
||||
namespace lgfx
|
||||
@@ -450,7 +450,11 @@ class TOUCH_CHSC6X : public ITouch
|
||||
bool init(void) override
|
||||
{
|
||||
if (chsc6xTouch == nullptr) {
|
||||
#if (TOUCH_I2C_PORT == 1)
|
||||
chsc6xTouch = new chsc6x(&Wire1, TOUCH_SDA_PIN, TOUCH_SCL_PIN, TOUCH_INT_PIN, TOUCH_RST_PIN);
|
||||
#else
|
||||
chsc6xTouch = new chsc6x(&Wire, TOUCH_SDA_PIN, TOUCH_SCL_PIN, TOUCH_INT_PIN, TOUCH_RST_PIN);
|
||||
#endif
|
||||
}
|
||||
chsc6xTouch->chsc6x_init();
|
||||
return true;
|
||||
@@ -487,7 +491,7 @@ class LGFX : public lgfx::LGFX_Device
|
||||
#if HAS_TOUCHSCREEN
|
||||
#if defined(T_WATCH_S3) || defined(ELECROW)
|
||||
lgfx::Touch_FT5x06 _touch_instance;
|
||||
#elif defined(HELTEC_V4_TFT)
|
||||
#elif defined(HELTEC_V4_TFT) || defined(HELTEC_V4_R8_TFT)
|
||||
lgfx::TOUCH_CHSC6X _touch_instance;
|
||||
#else
|
||||
lgfx::Touch_GT911 _touch_instance;
|
||||
@@ -506,7 +510,11 @@ class LGFX : public lgfx::LGFX_Device
|
||||
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing
|
||||
// 80MHz by an integer)
|
||||
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
|
||||
cfg.spi_3wire = false;
|
||||
#ifdef SPI_3_WIRE
|
||||
cfg.spi_3wire = SPI_3_WIRE;
|
||||
#else
|
||||
cfg.spi_3wire = true; // Set to true if reception is done on the MOSI pin
|
||||
#endif
|
||||
cfg.use_lock = true; // Set to true to use transaction locking
|
||||
cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch /
|
||||
// SPI_DMA_CH_AUTO=auto setting)
|
||||
@@ -556,8 +564,11 @@ class LGFX : public lgfx::LGFX_Device
|
||||
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
|
||||
cfg.dlen_16bit =
|
||||
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
|
||||
#if defined(HAS_SDCARD)
|
||||
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
|
||||
|
||||
#else
|
||||
cfg.bus_shared = false;
|
||||
#endif
|
||||
// Set the following only when the display is shifted with a driver with a variable number of pixels, such as the
|
||||
// ST7735 or ILI9163.
|
||||
// cfg.memory_width = TFT_WIDTH; // Maximum width supported by the driver IC
|
||||
|
||||
@@ -390,8 +390,11 @@ void InputBroker::Init()
|
||||
seesawRotary = nullptr;
|
||||
}
|
||||
}
|
||||
#ifdef __linux__
|
||||
// Linux evdev keyboard input only — macOS has no <linux/input.h>.
|
||||
aLinuxInputImpl = new LinuxInputImpl();
|
||||
aLinuxInputImpl->init();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_INPUTBROKER && HAS_TRACKBALL
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
#if ARCH_PORTDUINO
|
||||
// Linux evdev keyboard input. Only compiled on Linux portduino targets;
|
||||
// macOS / non-Linux builds have no <linux/input.h> or epoll, and the
|
||||
// headless build doesn't need real keyboards anyway.
|
||||
#if ARCH_PORTDUINO && defined(__linux__)
|
||||
#include "InputBroker.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include <assert.h>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#ifdef ARCH_PORTDUINO
|
||||
// Linux evdev impl. Same Linux-only gating as LinuxInput.h.
|
||||
#if defined(ARCH_PORTDUINO) && defined(__linux__)
|
||||
#pragma once
|
||||
#include "LinuxInput.h"
|
||||
#include "main.h"
|
||||
|
||||
+18
-8
@@ -2,18 +2,21 @@
|
||||
|
||||
#include "meshUtils.h"
|
||||
|
||||
// Convert seconds to ms, clamping at INT32_MAX (~24.86 days)
|
||||
static inline uint32_t secondsToMsClamped(uint32_t secs)
|
||||
{
|
||||
constexpr uint32_t MAX_MS = static_cast<uint32_t>(INT32_MAX);
|
||||
return (secs > MAX_MS / 1000U) ? MAX_MS : secs * 1000U;
|
||||
}
|
||||
|
||||
uint32_t Default::getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval)
|
||||
{
|
||||
if (configuredInterval > 0)
|
||||
return configuredInterval * 1000;
|
||||
return defaultInterval * 1000;
|
||||
return secondsToMsClamped(configuredInterval > 0 ? configuredInterval : defaultInterval);
|
||||
}
|
||||
|
||||
uint32_t Default::getConfiguredOrDefaultMs(uint32_t configuredInterval)
|
||||
{
|
||||
if (configuredInterval > 0)
|
||||
return configuredInterval * 1000;
|
||||
return default_broadcast_interval_secs * 1000;
|
||||
return secondsToMsClamped(configuredInterval > 0 ? configuredInterval : default_broadcast_interval_secs);
|
||||
}
|
||||
|
||||
uint32_t Default::getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue)
|
||||
@@ -47,7 +50,14 @@ uint32_t Default::getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t d
|
||||
meshtastic_Config_DeviceConfig_Role_TAK_TRACKER))
|
||||
return getConfiguredOrDefaultMs(configured, defaultValue);
|
||||
|
||||
return getConfiguredOrDefaultMs(configured, defaultValue) * congestionScalingCoefficient(numOnlineNodes);
|
||||
// Saturate at INT32_MAX to match secondsToMsClamped: float→uint32_t when
|
||||
// out of range is UB, and the result is consumed as an int32_t downstream.
|
||||
constexpr uint32_t MAX_MS = static_cast<uint32_t>(INT32_MAX);
|
||||
uint32_t base = getConfiguredOrDefaultMs(configured, defaultValue);
|
||||
float coef = congestionScalingCoefficient(numOnlineNodes);
|
||||
if (static_cast<double>(base) * static_cast<double>(coef) >= static_cast<double>(MAX_MS))
|
||||
return MAX_MS;
|
||||
return base * coef;
|
||||
}
|
||||
|
||||
uint32_t Default::getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue)
|
||||
@@ -66,4 +76,4 @@ uint8_t Default::getConfiguredOrDefaultHopLimit(uint8_t configured)
|
||||
#else
|
||||
return (configured >= HOP_MAX) ? HOP_MAX : config.lora.hop_limit;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,12 @@ extern Adafruit_nRFCrypto nRFCrypto;
|
||||
#include <Arduino.h>
|
||||
#elif defined(ARCH_PORTDUINO)
|
||||
#include <random>
|
||||
#include <sys/random.h>
|
||||
#include <unistd.h>
|
||||
#ifdef __linux__
|
||||
#include <sys/random.h> // getrandom()
|
||||
#else
|
||||
#include <stdlib.h> // arc4random_buf() on Darwin/BSD
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace HardwareRNG
|
||||
@@ -119,10 +123,16 @@ bool fill(uint8_t *buffer, size_t length, bool useRadioEntropy)
|
||||
filled = true;
|
||||
#elif defined(ARCH_PORTDUINO)
|
||||
// Prefer the host OS RNG first when running under Portduino.
|
||||
#ifdef __linux__
|
||||
ssize_t generated = ::getrandom(buffer, length, 0);
|
||||
if (generated == static_cast<ssize_t>(length)) {
|
||||
filled = true;
|
||||
}
|
||||
#else
|
||||
// arc4random_buf is available on Darwin/BSD and cannot fail.
|
||||
::arc4random_buf(buffer, length);
|
||||
filled = true;
|
||||
#endif
|
||||
|
||||
if (!filled) {
|
||||
fillWithRandomDevice(buffer, length);
|
||||
|
||||
@@ -6,6 +6,33 @@
|
||||
#include "configuration.h"
|
||||
#include "detect/LoRaRadioType.h"
|
||||
|
||||
// Sentinel marking the end of a modem preset array. Declared `const` rather
|
||||
// than `constexpr` because the cast from 0xFF to the enum is out-of-range and
|
||||
// therefore not a valid constant expression on Clang 16+ (Apple Clang on
|
||||
// macOS). The value is only ever compared at runtime, so static-init is fine.
|
||||
static const meshtastic_Config_LoRaConfig_ModemPreset MODEM_PRESET_END =
|
||||
static_cast<meshtastic_Config_LoRaConfig_ModemPreset>(0xFF);
|
||||
|
||||
// Region profile: bundles the preset list with regulatory parameters shared across regions
|
||||
struct RegionProfile {
|
||||
const meshtastic_Config_LoRaConfig_ModemPreset *presets; // sentinel-terminated; first entry is the default
|
||||
float spacing; // gaps between radio channels
|
||||
float padding; // padding at each side of the "operating channel"
|
||||
bool audioPermitted;
|
||||
bool licensedOnly; // a region profile for licensed operators only
|
||||
int8_t textThrottle; // throttle for text - future expansion
|
||||
int8_t positionThrottle; // throttle for location data - future expansion
|
||||
int8_t telemetryThrottle; // throttle for telemetry - future expansion
|
||||
uint8_t overrideSlot; // a per-region override slot for if we need to fix it in place
|
||||
};
|
||||
|
||||
extern const RegionProfile PROFILE_STD;
|
||||
extern const RegionProfile PROFILE_EU868;
|
||||
extern const RegionProfile PROFILE_UNDEF;
|
||||
// extern const RegionProfile PROFILE_LITE;
|
||||
// extern const RegionProfile PROFILE_NARROW;
|
||||
// extern const RegionProfile PROFILE_HAM;
|
||||
|
||||
// Map from old region names to new region enums
|
||||
struct RegionInfo {
|
||||
meshtastic_Config_LoRaConfig_RegionCode code;
|
||||
|
||||
@@ -37,8 +37,8 @@ enum RxSource {
|
||||
**/
|
||||
#define HOP_MAX 7
|
||||
|
||||
/// We normally just use max 3 hops for sending reliable messages
|
||||
#define HOP_RELIABLE 3
|
||||
/// Hamvention 2026!
|
||||
#define HOP_RELIABLE 4
|
||||
|
||||
// For old firmware or when falling back to flooding, there is no next-hop preference
|
||||
#define NO_NEXT_HOP_PREFERENCE 0
|
||||
|
||||
@@ -140,10 +140,10 @@ bool NextHopRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p)
|
||||
LOG_INFO("favorite-ROUTER/CLIENT_BASE-to-ROUTER/CLIENT_BASE rebroadcast: preserving hop_limit");
|
||||
}
|
||||
#if USERPREFS_EVENT_MODE
|
||||
if (tosend->hop_limit > 2) {
|
||||
if (tosend->hop_limit > 3) {
|
||||
// if we are "correcting" the hop_limit, "correct" the hop_start by the same amount to preserve hops away.
|
||||
tosend->hop_start -= (tosend->hop_limit - 2);
|
||||
tosend->hop_limit = 2;
|
||||
tosend->hop_start -= (tosend->hop_limit - 3);
|
||||
tosend->hop_limit = 3;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+3
-3
@@ -688,7 +688,7 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
|
||||
strncpy(config.network.ntp_server, "meshtastic.pool.ntp.org", 32);
|
||||
|
||||
#if (defined(T_DECK) || defined(T_WATCH_S3) || defined(UNPHONE) || defined(PICOMPUTER_S3) || defined(SENSECAP_INDICATOR) || \
|
||||
defined(ELECROW_PANEL) || defined(HELTEC_V4_TFT)) && \
|
||||
defined(ELECROW_PANEL) || defined(HELTEC_V4_TFT) || defined(HELTEC_V4_R8_TFT)) && \
|
||||
HAS_TFT
|
||||
// switch BT off by default; use TFT programming mode or hotkey to enable
|
||||
config.bluetooth.enabled = false;
|
||||
@@ -1205,11 +1205,11 @@ void NodeDB::loadFromDisk()
|
||||
spiLock->unlock();
|
||||
#endif
|
||||
#ifdef FSCom
|
||||
#ifdef FACTORY_INSTALL
|
||||
#if defined(FACTORY_INSTALL) && !defined(ARCH_PORTDUINO)
|
||||
spiLock->lock();
|
||||
if (!FSCom.exists("/prefs/" xstr(BUILD_EPOCH))) {
|
||||
LOG_WARN("Factory Install Reset!");
|
||||
FSCom.format();
|
||||
rmDir("/prefs");
|
||||
FSCom.mkdir("/prefs");
|
||||
File f2 = FSCom.open("/prefs/" xstr(BUILD_EPOCH), FILE_O_WRITE);
|
||||
if (f2) {
|
||||
|
||||
@@ -109,7 +109,7 @@ bool RadioLibInterface::canSendImmediately()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RadioLibInterface::receiveDetected(uint16_t irq, ulong syncWordHeaderValidFlag, ulong preambleDetectedFlag)
|
||||
bool RadioLibInterface::receiveDetected(uint16_t irq, unsigned long syncWordHeaderValidFlag, unsigned long preambleDetectedFlag)
|
||||
{
|
||||
bool detected = (irq & (syncWordHeaderValidFlag | preambleDetectedFlag));
|
||||
// Handle false detections
|
||||
|
||||
@@ -213,7 +213,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
protected:
|
||||
uint32_t activeReceiveStart = 0;
|
||||
|
||||
bool receiveDetected(uint16_t irq, ulong syncWordHeaderValidFlag, ulong preambleDetectedFlag);
|
||||
bool receiveDetected(uint16_t irq, unsigned long syncWordHeaderValidFlag, unsigned long preambleDetectedFlag);
|
||||
|
||||
/** Do any hardware setup needed on entry into send configuration for the radio.
|
||||
* Subclasses can customize, but must also call this base method */
|
||||
|
||||
+7
-4
@@ -719,9 +719,13 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
||||
// Also, we should set the time from the ISR and it should have msec level resolution
|
||||
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
||||
|
||||
// Store a copy of encrypted packet for MQTT
|
||||
// Store a copy of the encrypted packet for MQTT.
|
||||
// Local, not a class member: handleReceived re-enters itself when a module
|
||||
// reply broadcast goes through MeshService::sendToMesh -> Router::sendLocal,
|
||||
// and a member would be silently overwritten without release on the inner
|
||||
// call. Each invocation now owns its own copy (issue #9632, #10101, #8729).
|
||||
DEBUG_HEAP_BEFORE;
|
||||
p_encrypted = packetPool.allocCopy(*p);
|
||||
meshtastic_MeshPacket *p_encrypted = packetPool.allocCopy(*p);
|
||||
DEBUG_HEAP_AFTER("Router::handleReceived", p_encrypted);
|
||||
|
||||
// Take those raw bytes and convert them back into a well structured protobuf we can understand
|
||||
@@ -815,8 +819,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
||||
#endif
|
||||
}
|
||||
|
||||
packetPool.release(p_encrypted); // Release the encrypted packet
|
||||
p_encrypted = nullptr;
|
||||
packetPool.release(p_encrypted); // Release the encrypted packet (release() handles nullptr)
|
||||
}
|
||||
|
||||
void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
|
||||
|
||||
@@ -92,9 +92,6 @@ class Router : protected concurrency::OSThread, protected PacketHistory
|
||||
before us */
|
||||
uint32_t rxDupe = 0, txRelayCanceled = 0;
|
||||
|
||||
// pointer to the encrypted packet
|
||||
meshtastic_MeshPacket *p_encrypted = nullptr;
|
||||
|
||||
protected:
|
||||
friend class RoutingModule;
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@
|
||||
#include <assert.h>
|
||||
#include <utility>
|
||||
|
||||
#include <IPAddress.h>
|
||||
#include "IPAddress.h"
|
||||
#if defined(ARCH_PORTDUINO)
|
||||
#include <netinet/in.h>
|
||||
#elif !defined(ntohl)
|
||||
|
||||
@@ -9,13 +9,10 @@
|
||||
#include "PortduinoGlue.h"
|
||||
#include "SHA256.h"
|
||||
#include "api/ServerAPI.h"
|
||||
#include "linux/gpio/LinuxGPIOPin.h"
|
||||
#include "meshUtils.h"
|
||||
#include <ErriezCRC32.h>
|
||||
#include <Utility.h>
|
||||
#include <assert.h>
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@@ -25,6 +22,12 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef PORTDUINO_LINUX_HARDWARE
|
||||
#include "linux/gpio/LinuxGPIOPin.h"
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/hci.h>
|
||||
#endif
|
||||
|
||||
#ifdef PORTDUINO_LINUX_HARDWARE
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
@@ -130,9 +133,9 @@ void getMacAddr(uint8_t *dmac)
|
||||
}
|
||||
} else if (portduino_config.mac_address.length() > 11) {
|
||||
MAC_from_string(portduino_config.mac_address, dmac);
|
||||
exit;
|
||||
return;
|
||||
} else {
|
||||
|
||||
#ifdef PORTDUINO_LINUX_HARDWARE
|
||||
struct hci_dev_info di = {0};
|
||||
di.dev_id = 0;
|
||||
bdaddr_t bdaddr;
|
||||
@@ -152,6 +155,11 @@ void getMacAddr(uint8_t *dmac)
|
||||
dmac[3] = di.bdaddr.b[2];
|
||||
dmac[4] = di.bdaddr.b[1];
|
||||
dmac[5] = di.bdaddr.b[0];
|
||||
#else
|
||||
// No BlueZ on non-Linux hosts (e.g. macOS). Leave dmac at its default;
|
||||
// the caller can override via the --hwid CLI flag or the YAML config.
|
||||
(void)dmac;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,10 +490,17 @@ void portduinoSetup()
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
char serial[9] = {0};
|
||||
ch341Hal->getSerialString(serial, 8);
|
||||
// Pass the full buffer size (9 = 8 chars + null) to getSerialString,
|
||||
// not 8. The function treats `len` as buffer size and reserves one
|
||||
// slot for the null terminator, so passing 8 produced a 7-char serial
|
||||
// and broke the `strlen(serial) == 8` check below — masked on Linux
|
||||
// by the BlueZ HCI MAC fallback in getMacAddr(), but on macOS (where
|
||||
// the BlueZ path is __linux__-guarded) it left mac_address empty and
|
||||
// meshtasticd refused to start.
|
||||
ch341Hal->getSerialString(serial, sizeof(serial));
|
||||
std::cout << "CH341 Serial " << serial << std::endl;
|
||||
char product_string[96] = {0};
|
||||
ch341Hal->getProductString(product_string, 95);
|
||||
ch341Hal->getProductString(product_string, sizeof(product_string));
|
||||
std::cout << "CH341 Product " << product_string << std::endl;
|
||||
if (strlen(serial) == 8 && portduino_config.mac_address.length() < 12) {
|
||||
std::cout << "Deriving MAC address from Serial and Product String" << std::endl;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#include <RadioLib.h>
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <libpinedio-usb.h>
|
||||
#include <unistd.h>
|
||||
@@ -34,7 +35,7 @@ class Ch341Hal : public RadioLibHal
|
||||
: RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING)
|
||||
{
|
||||
if (serial != "") {
|
||||
strncpy(pinedio.serial_number, serial.c_str(), 8);
|
||||
std::strncpy(pinedio.serial_number, serial.c_str(), 8);
|
||||
pinedio_set_option(&pinedio, PINEDIO_OPTION_SEARCH_SERIAL, 1);
|
||||
}
|
||||
// LOG_INFO("USB Serial: %s", pinedio.serial_number);
|
||||
@@ -59,8 +60,11 @@ class Ch341Hal : public RadioLibHal
|
||||
|
||||
void getSerialString(char *_serial, size_t len)
|
||||
{
|
||||
len = len > 8 ? 8 : len;
|
||||
strncpy(_serial, pinedio.serial_number, len);
|
||||
if (len == 0)
|
||||
return;
|
||||
size_t bytesCopied = (len - 1) < 8 ? (len - 1) : 8;
|
||||
std::strncpy(_serial, pinedio.serial_number, bytesCopied);
|
||||
_serial[bytesCopied] = '\0';
|
||||
}
|
||||
|
||||
void getProductString(char *_product_string, size_t len)
|
||||
|
||||
@@ -127,6 +127,60 @@ void test_client_uses_public_channel_minimums()
|
||||
TEST_ASSERT_EQUAL_UINT32(60 * 60, position);
|
||||
}
|
||||
|
||||
// --- Saturation/clamp tests for getConfiguredOrDefaultMs[Scaled] ---
|
||||
// These guard the INT32_MAX clamp added to avoid uint32 wrap of secs*1000 and
|
||||
// to keep results safe to cast to int32_t for OSThread runOnce returns.
|
||||
|
||||
void test_ms_below_threshold()
|
||||
{
|
||||
// Ordinary value passes through unchanged.
|
||||
TEST_ASSERT_EQUAL_UINT32(60000U, Default::getConfiguredOrDefaultMs(60, 0));
|
||||
}
|
||||
|
||||
void test_ms_at_threshold()
|
||||
{
|
||||
// INT32_MAX / 1000 = 2,147,483 — largest secs that does not clamp.
|
||||
TEST_ASSERT_EQUAL_UINT32(2147483000U, Default::getConfiguredOrDefaultMs(2147483U, 0));
|
||||
}
|
||||
|
||||
void test_ms_just_above_threshold()
|
||||
{
|
||||
// One second over the boundary must saturate, not wrap.
|
||||
TEST_ASSERT_EQUAL_UINT32(static_cast<uint32_t>(INT32_MAX), Default::getConfiguredOrDefaultMs(2147484U, 0));
|
||||
}
|
||||
|
||||
void test_ms_uint32_max()
|
||||
{
|
||||
// default_sds_secs == UINT32_MAX on non-routers must not wrap.
|
||||
TEST_ASSERT_EQUAL_UINT32(static_cast<uint32_t>(INT32_MAX), Default::getConfiguredOrDefaultMs(UINT32_MAX, 0));
|
||||
}
|
||||
|
||||
void test_ms_default_clamps()
|
||||
{
|
||||
// Clamp also applies when the default-arg path is taken (configured == 0).
|
||||
TEST_ASSERT_EQUAL_UINT32(static_cast<uint32_t>(INT32_MAX), Default::getConfiguredOrDefaultMs(0, UINT32_MAX));
|
||||
}
|
||||
|
||||
void test_ms_result_is_int32_safe()
|
||||
{
|
||||
// Regression guard for runOnce returns: cast to int32_t must not go negative.
|
||||
int32_t result = static_cast<int32_t>(Default::getConfiguredOrDefaultMs(UINT32_MAX, 0));
|
||||
TEST_ASSERT_GREATER_OR_EQUAL_INT32(0, result);
|
||||
}
|
||||
|
||||
void test_scaled_overflow_saturates()
|
||||
{
|
||||
// long_fast (SF11/BW250) with a 24h base and heavy congestion overflows
|
||||
// the uint32 result without the double-precision guard. Must saturate.
|
||||
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
|
||||
config.lora.use_preset = false;
|
||||
config.lora.spread_factor = 11;
|
||||
config.lora.bandwidth = 250;
|
||||
|
||||
uint32_t res = Default::getConfiguredOrDefaultMsScaled(0, ONE_DAY, 1000);
|
||||
TEST_ASSERT_EQUAL_UINT32(static_cast<uint32_t>(INT32_MAX), res);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Small delay to match other test mains
|
||||
@@ -140,6 +194,13 @@ void setup()
|
||||
RUN_TEST(test_router_uses_router_minimums);
|
||||
RUN_TEST(test_router_late_uses_router_minimums);
|
||||
RUN_TEST(test_client_uses_public_channel_minimums);
|
||||
RUN_TEST(test_ms_below_threshold);
|
||||
RUN_TEST(test_ms_at_threshold);
|
||||
RUN_TEST(test_ms_just_above_threshold);
|
||||
RUN_TEST(test_ms_uint32_max);
|
||||
RUN_TEST(test_ms_default_clamps);
|
||||
RUN_TEST(test_ms_result_is_int32_safe);
|
||||
RUN_TEST(test_scaled_overflow_saturates);
|
||||
exit(UNITY_END());
|
||||
}
|
||||
|
||||
|
||||
+23
-22
@@ -1,30 +1,30 @@
|
||||
{
|
||||
// "USERPREFS_BUTTON_PIN": "36",
|
||||
// "USERPREFS_CHANNELS_TO_WRITE": "3",
|
||||
"USERPREFS_CHANNELS_TO_WRITE": "3",
|
||||
// "USERPREFS_CHANNEL_0_DOWNLINK_ENABLED": "false",
|
||||
// "USERPREFS_CHANNEL_0_NAME": "REPLACEME",
|
||||
// "USERPREFS_CHANNEL_0_PRECISION": "14",
|
||||
// "USERPREFS_CHANNEL_0_PSK": "{ 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 }",
|
||||
// "USERPREFS_CHANNEL_0_UPLINK_ENABLED": "true",
|
||||
"USERPREFS_CHANNEL_0_NAME": "Hamvention",
|
||||
"USERPREFS_CHANNEL_0_PRECISION": "14",
|
||||
"USERPREFS_CHANNEL_0_PSK": "{ 0x38, 0x4b, 0xbc, 0xc0, 0x1d, 0xc0, 0x22, 0xd1, 0x81, 0xbf, 0x36, 0xb8, 0x61, 0x21, 0xe1, 0xfb, 0x96, 0xb7, 0x2e, 0x55, 0xbf, 0x74, 0x22, 0x7e, 0x9d, 0x6a, 0xfb, 0x48, 0xd6, 0x4c, 0xb1, 0xa1 }",
|
||||
"USERPREFS_CHANNEL_0_UPLINK_ENABLED": "true",
|
||||
// "USERPREFS_CHANNEL_1_DOWNLINK_ENABLED": "false",
|
||||
// "USERPREFS_CHANNEL_1_NAME": "NodeChat",
|
||||
"USERPREFS_CHANNEL_1_NAME": "NodeChat",
|
||||
// "USERPREFS_CHANNEL_1_PRECISION": "14",
|
||||
// "USERPREFS_CHANNEL_1_PSK": "{ 0x4e, 0x22, 0x1d, 0x8b, 0xc3, 0x09, 0x1b, 0xe2, 0x11, 0x9c, 0x89, 0x12, 0xf2, 0x25, 0x19, 0x5d, 0x15, 0x3e, 0x30, 0x7b, 0x86, 0xb6, 0xec, 0xc4, 0x6a, 0xc3, 0x96, 0x5e, 0x9e, 0x10, 0x9d, 0xd5 }",
|
||||
"USERPREFS_CHANNEL_1_PSK": "{ 0x4e, 0x22, 0x1d, 0x8b, 0xc3, 0x09, 0x1b, 0xe2, 0x11, 0x9c, 0x89, 0x12, 0xf2, 0x25, 0x19, 0x5d, 0x15, 0x3e, 0x30, 0x7b, 0x86, 0xb6, 0xec, 0xc4, 0x6a, 0xc3, 0x96, 0x5e, 0x9e, 0x10, 0x9d, 0xd5 }",
|
||||
// "USERPREFS_CHANNEL_1_UPLINK_ENABLED": "false",
|
||||
// "USERPREFS_CHANNEL_2_DOWNLINK_ENABLED": "false",
|
||||
// "USERPREFS_CHANNEL_2_NAME": "YardSale",
|
||||
"USERPREFS_CHANNEL_2_NAME": "YardSale",
|
||||
// "USERPREFS_CHANNEL_2_PRECISION": "14",
|
||||
// "USERPREFS_CHANNEL_2_PSK": "{ 0x15, 0x6f, 0xfe, 0x46, 0xd4, 0x56, 0x63, 0x8a, 0x54, 0x43, 0x13, 0xf2, 0xef, 0x6c, 0x63, 0x89, 0xf0, 0x06, 0x30, 0x52, 0xce, 0x36, 0x5e, 0xb1, 0xe8, 0xbb, 0x86, 0xe6, 0x26, 0x5b, 0x1d, 0x58 }",
|
||||
"USERPREFS_CHANNEL_2_PSK": "{ 0x15, 0x6f, 0xfe, 0x46, 0xd4, 0x56, 0x63, 0x8a, 0x54, 0x43, 0x13, 0xf2, 0xef, 0x6c, 0x63, 0x89, 0xf0, 0x06, 0x30, 0x52, 0xce, 0x36, 0x5e, 0xb1, 0xe8, 0xbb, 0x86, 0xe6, 0x26, 0x5b, 0x1d, 0x58 }",
|
||||
// "USERPREFS_CHANNEL_2_UPLINK_ENABLED": "false",
|
||||
// "USERPREFS_CONFIG_GPS_MODE": "meshtastic_Config_PositionConfig_GpsMode_ENABLED",
|
||||
// "USERPREFS_CONFIG_LORA_IGNORE_MQTT": "true",
|
||||
"USERPREFS_CONFIG_LORA_IGNORE_MQTT": "true",
|
||||
// "USERPREFS_LORA_TX_DISABLED": "1", // If set, forces config.lora.tx_enabled=false during lora bootstrap
|
||||
// "USERPREFS_CONFIG_LORA_REGION": "meshtastic_Config_LoRaConfig_RegionCode_US",
|
||||
"USERPREFS_CONFIG_LORA_REGION": "meshtastic_Config_LoRaConfig_RegionCode_US",
|
||||
// "USERPREFS_CONFIG_OWNER_LONG_NAME": "My Long Name",
|
||||
// "USERPREFS_CONFIG_OWNER_SHORT_NAME": "MLN",
|
||||
// "USERPREFS_CONFIG_DEVICE_ROLE": "meshtastic_Config_DeviceConfig_Role_CLIENT", // Defaults to CLIENT. ROUTER*, and LOST AND FOUND roles are restricted.
|
||||
// "USERPREFS_EVENT_MODE": "1",
|
||||
// "USERPREFS_FIRMWARE_EDITION": "meshtastic_FirmwareEdition_BURNING_MAN",
|
||||
"USERPREFS_EVENT_MODE": "1",
|
||||
"USERPREFS_FIRMWARE_EDITION": "meshtastic_FirmwareEdition_HAMVENTION",
|
||||
// "USERPREFS_FIXED_BLUETOOTH": "121212",
|
||||
// "USERPREFS_FIXED_GPS": "",
|
||||
// "USERPREFS_FIXED_GPS_ALT": "0",
|
||||
@@ -34,16 +34,16 @@
|
||||
// "USERPREFS_CONFIG_GPS_UPDATE_INTERVAL": "600",
|
||||
// "USERPREFS_CONFIG_POSITION_BROADCAST_INTERVAL": "1800",
|
||||
// "USERPREFS_CONFIG_DEVICE_TELEM_UPDATE_INTERVAL": "900", // Device telemetry update interval in seconds
|
||||
// "USERPREFS_LORACONFIG_CHANNEL_NUM": "31",
|
||||
// "USERPREFS_LORACONFIG_MODEM_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST",
|
||||
"USERPREFS_LORACONFIG_CHANNEL_NUM": "31",
|
||||
"USERPREFS_LORACONFIG_MODEM_PRESET": "meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO",
|
||||
// "USERPREFS_USE_ADMIN_KEY_0": "{ 0xcd, 0xc0, 0xb4, 0x3c, 0x53, 0x24, 0xdf, 0x13, 0xca, 0x5a, 0xa6, 0x0c, 0x0d, 0xec, 0x85, 0x5a, 0x4c, 0xf6, 0x1a, 0x96, 0x04, 0x1a, 0x3e, 0xfc, 0xbb, 0x8e, 0x33, 0x71, 0xe5, 0xfc, 0xff, 0x3c }",
|
||||
// "USERPREFS_USE_ADMIN_KEY_1": "{}",
|
||||
// "USERPREFS_USE_ADMIN_KEY_2": "{}",
|
||||
// "USERPREFS_OEM_TEXT": "Caterham Car Club",
|
||||
// "USERPREFS_OEM_FONT_SIZE": "0",
|
||||
// "USERPREFS_OEM_IMAGE_WIDTH": "50",
|
||||
// "USERPREFS_OEM_IMAGE_HEIGHT": "28",
|
||||
// "USERPREFS_OEM_IMAGE_DATA": "{ 0x00, 0x00, 0xF0, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x80, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0x61, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x0C, 0xFF, 0xFF, 0xC7, 0x00, 0x00, 0x00, 0x18, 0xFF, 0xFF, 0x67, 0x00, 0x00, 0x00, 0x18, 0x1F, 0xF0, 0x67, 0x00, 0x00, 0x00, 0x30, 0x1F, 0xF8, 0x33, 0x00, 0x00, 0x00, 0x30, 0x00, 0xFC, 0x31, 0x00, 0x00, 0x00, 0x60, 0x00, 0xFE, 0x18, 0x00, 0x00, 0x00, 0x60, 0x00, 0x7E, 0x18, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x3F, 0x0C, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x1F, 0x0C, 0x00, 0x00, 0x00, 0x80, 0x81, 0x1F, 0x06, 0x00, 0x00, 0x00, 0x80, 0xC1, 0x0F, 0x06, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x8F, 0x01, 0x00, 0x00, 0x00, 0x00, 0xEE, 0xC7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x00}",
|
||||
"USERPREFS_OEM_TEXT": "Hamvention 2026",
|
||||
"USERPREFS_OEM_FONT_SIZE": "0",
|
||||
"USERPREFS_OEM_IMAGE_WIDTH": "50",
|
||||
"USERPREFS_OEM_IMAGE_HEIGHT": "28",
|
||||
"USERPREFS_OEM_IMAGE_DATA": "{ 0x00, 0xf0, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x7f, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0xec, 0x07, 0xf0, 0x00, 0x60, 0x00, 0x00, 0xd8, 0x3b, 0x88, 0x00, 0x30, 0x00, 0x00, 0xa0, 0xe7, 0x88, 0x00, 0x18, 0x08, 0x30, 0xc0, 0xe8, 0x87, 0x01, 0x0c, 0x1c, 0x38, 0x80, 0xde, 0x03, 0x02, 0x04, 0x1e, 0x78, 0x00, 0xbd, 0x05, 0x02, 0x02, 0x0e, 0xfc, 0x00, 0x7b, 0x36, 0x03, 0x02, 0x07, 0xfe, 0x00, 0x36, 0xcf, 0x00, 0x01, 0x07, 0xce, 0x01, 0xde, 0x06, 0x00, 0x81, 0x03, 0xc7, 0x03, 0xe6, 0x06, 0x00, 0xc1, 0x81, 0x87, 0x03, 0xec, 0x04, 0x00, 0xc1, 0x81, 0x03, 0x07, 0xec, 0x02, 0x00, 0xe1, 0xc0, 0x01, 0x07, 0x7c, 0x02, 0x00, 0x71, 0xe0, 0x01, 0x0e, 0xbc, 0x01, 0x00, 0x71, 0xe0, 0x00, 0x1c, 0xce, 0x00, 0x00, 0x39, 0x70, 0x00, 0x1c, 0xf6, 0x00, 0x00, 0x12, 0x70, 0x00, 0x38, 0x6a, 0x00, 0x00, 0x02, 0x20, 0x00, 0x30, 0x2f, 0x00, 0x00, 0x04, 0x00, 0x00, 0x90, 0x1f, 0x00, 0x00, 0x08, 0x00, 0x00, 0x80, 0x0e, 0x00, 0x00, 0x18, 0x00, 0x00, 0xc0, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0xc0, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x1c, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0x00, 0x00, 0x00, 0x00 }",
|
||||
// "USERPREFS_NETWORK_ENABLED_PROTOCOLS": "1", // Enable UDP mesh
|
||||
// "USERPREFS_NETWORK_WIFI_ENABLED": "true",
|
||||
// "USERPREFS_NETWORK_WIFI_SSID": "wifi_ssid",
|
||||
@@ -58,7 +58,8 @@
|
||||
// "USERPREFS_RINGTONE_NAG_SECS": "60",
|
||||
// "USERPREFS_NODEINFO_REPLY_SUPPRESS_SECS": "43200",
|
||||
// "USERPREFS_UI_TEST_LOG": "true", // Test-only: emits `Screen: frame N/M name=... reason=...` log per UI transition (for the mcp-server ui test tier); off in release builds.
|
||||
"USERPREFS_RINGTONE_RTTTL": "24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p",
|
||||
"USERPREFS_RINGTONE_RTTTL": "24:d=4,o=5,b=180:8e6,8d6,f#,g#,8c#6,8b,d,e,8b,8a,c#,e,2a",
|
||||
// "USERPREFS_NETWORK_IPV6_ENABLED": "1",
|
||||
"USERPREFS_TZ_STRING": "tzplaceholder "
|
||||
"USERPREFS_TZ_STRING": "EST5EDT,M3.2.0,M11.1.0",
|
||||
"FACTORY_INSTALL": "1"
|
||||
}
|
||||
|
||||
@@ -89,8 +89,10 @@ build_flags =
|
||||
-D VIEW_240x320
|
||||
-D DISPLAY_SET_RESOLUTION
|
||||
-D DISPLAY_SIZE=240x320 ; portrait mode
|
||||
-D LGFX_SPI_3WIRE=true
|
||||
-D LGFX_PIN_SCK=17
|
||||
-D LGFX_PIN_MOSI=33
|
||||
-D LGFX_PIN_MISO=-1
|
||||
-D LGFX_PIN_DC=16
|
||||
-D LGFX_PIN_CS=15
|
||||
-D LGFX_PIN_BL=21
|
||||
@@ -123,7 +125,7 @@ build_flags =
|
||||
-D SCREEN_TRANSITION_FRAMERATE=5
|
||||
-D BRIGHTNESS_DEFAULT=130 ; Medium Low Brightness
|
||||
-D HAS_TOUCHSCREEN=1
|
||||
-D TOUCH_I2C_PORT=0
|
||||
-D TOUCH_I2C_PORT=1
|
||||
-D TOUCH_SLAVE_ADDRESS=0x2E
|
||||
-D SCREEN_TOUCH_INT=TOUCH_INT_PIN
|
||||
-D SCREEN_TOUCH_RST=TOUCH_RST_PIN
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define USB_VID 0x303a
|
||||
#define USB_PID 0x1001
|
||||
|
||||
static const uint8_t TX = 43;
|
||||
static const uint8_t RX = 44;
|
||||
|
||||
static const uint8_t SDA = 17;
|
||||
static const uint8_t SCL = 18;
|
||||
|
||||
static const uint8_t SS = 8;
|
||||
static const uint8_t MOSI = 10;
|
||||
static const uint8_t MISO = 11;
|
||||
static const uint8_t SCK = 9;
|
||||
|
||||
static const uint8_t A0 = 1;
|
||||
static const uint8_t A1 = 2;
|
||||
static const uint8_t A2 = 3;
|
||||
static const uint8_t A3 = 4;
|
||||
static const uint8_t A4 = 5;
|
||||
static const uint8_t A5 = 6;
|
||||
static const uint8_t A6 = 7;
|
||||
static const uint8_t A7 = 8;
|
||||
static const uint8_t A8 = 9;
|
||||
static const uint8_t A9 = 10;
|
||||
static const uint8_t A10 = 11;
|
||||
static const uint8_t A11 = 12;
|
||||
static const uint8_t A12 = 13;
|
||||
static const uint8_t A13 = 14;
|
||||
static const uint8_t A14 = 15;
|
||||
static const uint8_t A15 = 16;
|
||||
static const uint8_t A16 = 17;
|
||||
static const uint8_t A17 = 18;
|
||||
static const uint8_t A18 = 19;
|
||||
static const uint8_t A19 = 20;
|
||||
|
||||
static const uint8_t T1 = 1;
|
||||
static const uint8_t T2 = 2;
|
||||
static const uint8_t T3 = 3;
|
||||
static const uint8_t T4 = 4;
|
||||
static const uint8_t T5 = 5;
|
||||
static const uint8_t T6 = 6;
|
||||
static const uint8_t T7 = 7;
|
||||
static const uint8_t T8 = 8;
|
||||
static const uint8_t T9 = 9;
|
||||
static const uint8_t T10 = 10;
|
||||
static const uint8_t T11 = 11;
|
||||
static const uint8_t T12 = 12;
|
||||
static const uint8_t T13 = 13;
|
||||
static const uint8_t T14 = 14;
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
@@ -0,0 +1,145 @@
|
||||
[heltec_v4_r8_base]
|
||||
extends = esp32s3_base
|
||||
board = heltec_v4_r8
|
||||
board_check = true
|
||||
board_build.partitions = default_16MB.csv
|
||||
build_flags =
|
||||
${esp32s3_base.build_flags}
|
||||
-D HELTEC_V4_R8
|
||||
-D HAS_LORA_FEM=1
|
||||
-D BOARD_HAS_PSRAM
|
||||
-I variants/esp32s3/heltec_v4_r8
|
||||
-ULED_BUILTIN
|
||||
|
||||
[env:heltec-v4-r8-oled]
|
||||
custom_meshtastic_hw_model = 132
|
||||
custom_meshtastic_hw_model_slug = HELTEC_V4_R8
|
||||
custom_meshtastic_architecture = esp32-s3
|
||||
custom_meshtastic_actively_supported = true
|
||||
custom_meshtastic_support_level = 1
|
||||
custom_meshtastic_display_name = Heltec V4 R8
|
||||
custom_meshtastic_images = heltec_v4_r8.svg
|
||||
custom_meshtastic_tags = Heltec
|
||||
custom_meshtastic_requires_dfu = true
|
||||
custom_meshtastic_partition_scheme = 16MB
|
||||
|
||||
extends = heltec_v4_r8_base
|
||||
build_flags =
|
||||
${heltec_v4_r8_base.build_flags}
|
||||
-D HELTEC_V4_R8_OLED
|
||||
-D USE_SSD1306 ; Heltec_v4_R8 has an SSD1315 display (compatible with SSD1306 driver)
|
||||
-D LED_POWER=46
|
||||
-D RESET_OLED=21
|
||||
-D I2C_SDA=17
|
||||
-D I2C_SCL=18
|
||||
|
||||
[env:heltec-v4-r8-tft]
|
||||
custom_meshtastic_hw_model = 132
|
||||
custom_meshtastic_hw_model_slug = HELTEC_V4_R8
|
||||
custom_meshtastic_architecture = esp32-s3
|
||||
custom_meshtastic_actively_supported = true
|
||||
custom_meshtastic_support_level = 1
|
||||
custom_meshtastic_display_name = Heltec V4 R8 TFT
|
||||
custom_meshtastic_images = heltec_v4_r8_tft.svg
|
||||
custom_meshtastic_tags = Heltec
|
||||
custom_meshtastic_requires_dfu = true
|
||||
custom_meshtastic_partition_scheme = 16MB
|
||||
|
||||
extends = heltec_v4_r8_base
|
||||
build_flags =
|
||||
${heltec_v4_r8_base.build_flags} ;-Os
|
||||
-D HELTEC_V4_R8_TFT
|
||||
-D I2C_SDA=17
|
||||
-D I2C_SCL=18
|
||||
-D PIN_BUTTON2=46
|
||||
-D ALT_BUTTON_PIN=PIN_BUTTON2
|
||||
-D ALT_BUTTON_ACTIVE_LOW=false
|
||||
-D PIN_BUZZER=4
|
||||
-D USE_PIN_BUZZER=PIN_BUZZER
|
||||
-D CONFIG_ARDUHAL_LOG_COLORS
|
||||
-D RADIOLIB_DEBUG_SPI=0
|
||||
-D RADIOLIB_DEBUG_PROTOCOL=0
|
||||
-D RADIOLIB_DEBUG_BASIC=0
|
||||
-D RADIOLIB_VERBOSE_ASSERT=0
|
||||
-D RADIOLIB_SPI_PARANOID=0
|
||||
-D CONFIG_DISABLE_HAL_LOCKS=1
|
||||
-D INPUTDRIVER_BUTTON_TYPE=0
|
||||
-D HAS_SCREEN=1
|
||||
-D HAS_TFT=1
|
||||
-D RAM_SIZE=5120
|
||||
-D LV_LVGL_H_INCLUDE_SIMPLE
|
||||
-D LV_CONF_INCLUDE_SIMPLE
|
||||
-D LV_COMP_CONF_INCLUDE_SIMPLE
|
||||
-D LV_USE_SYSMON=0
|
||||
-D LV_USE_PROFILER=0
|
||||
-D LV_USE_PERF_MONITOR=0
|
||||
-D LV_USE_MEM_MONITOR=0
|
||||
-D LV_USE_LOG=0
|
||||
-D LV_BUILD_TEST=0
|
||||
-D USE_LOG_DEBUG
|
||||
-D LOG_DEBUG_INC=\"DebugConfiguration.h\"
|
||||
-D USE_PACKET_API
|
||||
-D LGFX_DRIVER=LGFX_HELTEC_V4_TFT
|
||||
-D GFX_DRIVER_INC=\"graphics/LGFX/LGFX_HELTEC_V4_TFT.h\"
|
||||
-D VIEW_240x320
|
||||
-D DISPLAY_SET_RESOLUTION
|
||||
-D DISPLAY_SIZE=240x320 ; portrait mode
|
||||
-D LGFX_SPI_3WIRE=false
|
||||
-D LGFX_PIN_SCK=16
|
||||
-D LGFX_PIN_MOSI=15
|
||||
-D LGFX_PIN_MISO=45
|
||||
-D LGFX_PIN_DC=48
|
||||
-D LGFX_PIN_CS=47
|
||||
-D LGFX_PIN_BL=44
|
||||
-D LGFX_PIN_RST=21
|
||||
-D CUSTOM_TOUCH_DRIVER
|
||||
-D TOUCH_SDA_PIN=I2C_SDA
|
||||
-D TOUCH_SCL_PIN=I2C_SCL
|
||||
-D TOUCH_INT_PIN=-1
|
||||
-D TOUCH_RST_PIN=-1
|
||||
;base UI
|
||||
-D TFT_CS=LGFX_PIN_CS
|
||||
-D ST7789_CS=TFT_CS
|
||||
-D ST7789_RS=LGFX_PIN_DC
|
||||
-D ST7789_SDA=LGFX_PIN_MOSI
|
||||
-D ST7789_SCK=LGFX_PIN_SCK
|
||||
-D ST7789_RESET=LGFX_PIN_RST
|
||||
-D ST7789_MISO=LGFX_PIN_MISO
|
||||
-D ST7789_BUSY=-1
|
||||
-D ST7789_BL=LGFX_PIN_BL
|
||||
-D ST7789_SPI_HOST=SPI3_HOST
|
||||
-D TFT_BL=ST7789_BL
|
||||
-D SPI_FREQUENCY=75000000
|
||||
-D SPI_READ_FREQUENCY=SPI_FREQUENCY
|
||||
-D SPI_3_WIRE=false
|
||||
-D TFT_HEIGHT=320
|
||||
-D TFT_WIDTH=240
|
||||
-D TFT_OFFSET_X=0
|
||||
-D TFT_OFFSET_Y=0
|
||||
-D TFT_OFFSET_ROTATION=0
|
||||
-D SCREEN_ROTATE
|
||||
-D SCREEN_TRANSITION_FRAMERATE=5
|
||||
-D BRIGHTNESS_DEFAULT=130 ; Medium Low Brightness
|
||||
-D HAS_TOUCHSCREEN=1
|
||||
-D TOUCH_I2C_PORT=0
|
||||
-D TOUCH_SLAVE_ADDRESS=0x2E
|
||||
-D SCREEN_TOUCH_INT=TOUCH_INT_PIN
|
||||
-D SCREEN_TOUCH_RST=TOUCH_RST_PIN
|
||||
|
||||
; Have SPI interface SD card slot
|
||||
-D HAS_SDCARD
|
||||
-D SDCARD_USE_SPI1
|
||||
-D SDCARD_USER_SPI_BEGIN
|
||||
-D SPI_MOSI=LGFX_PIN_MOSI
|
||||
-D SPI_SCK=LGFX_PIN_SCK
|
||||
-D SPI_MISO=LGFX_PIN_MISO
|
||||
-D SPI_CS=3
|
||||
-D SDCARD_CS=SPI_CS
|
||||
-D SD_SPI_FREQUENCY=SPI_FREQUENCY
|
||||
|
||||
lib_deps = ${heltec_v4_r8_base.lib_deps}
|
||||
${device-ui_base.lib_deps}
|
||||
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
|
||||
lovyan03/LovyanGFX@1.2.19
|
||||
# renovate: datasource=git-refs depName=Quency-D_chsc6x packageName=https://github.com/Quency-D/chsc6x gitBranch=master
|
||||
https://github.com/Quency-D/chsc6x/archive/3b2b6cebf3177b3e2c33d06e07909b0b10159516.zip
|
||||
@@ -0,0 +1,72 @@
|
||||
#define VEXT_ENABLE 40 // active low, powers the oled display and the lora antenna boost
|
||||
#define VEXT_ON_VALUE LOW
|
||||
#define BUTTON_PIN 0
|
||||
|
||||
#define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
#define ADC_CHANNEL ADC1_GPIO1_CHANNEL
|
||||
#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider
|
||||
#define ADC_MULTIPLIER 4.9 * 1.035
|
||||
|
||||
#define USE_SX1262
|
||||
#define LORA_DIO0 -1 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 12
|
||||
#define LORA_DIO1 14 // SX1262 IRQ
|
||||
#define LORA_DIO2 13 // SX1262 BUSY
|
||||
#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TCXO is enabled
|
||||
|
||||
#define LORA_SCK 9
|
||||
#define LORA_MISO 11
|
||||
#define LORA_MOSI 10
|
||||
#define LORA_CS 8
|
||||
|
||||
#define SX126X_CS LORA_CS
|
||||
#define SX126X_DIO1 LORA_DIO1
|
||||
#define SX126X_BUSY LORA_DIO2
|
||||
#define SX126X_RESET LORA_RESET
|
||||
|
||||
#define SX126X_DIO2_AS_RF_SWITCH
|
||||
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||
|
||||
// Enable Traffic Management Module for Heltec V4
|
||||
#ifndef HAS_TRAFFIC_MANAGEMENT
|
||||
#define HAS_TRAFFIC_MANAGEMENT 1
|
||||
#endif
|
||||
#ifndef TRAFFIC_MANAGEMENT_CACHE_SIZE
|
||||
#define TRAFFIC_MANAGEMENT_CACHE_SIZE 2048
|
||||
#endif
|
||||
|
||||
// ---- KCT8103L RF FRONT END CONFIGURATION ----
|
||||
// The Heltec V4.3 uses a KCT8103L FEM chip with integrated PA and LNA
|
||||
// RF path: SX1262 -> Pi attenuator -> KCT8103L PA -> Antenna
|
||||
// Control logic (from KCT8103L datasheet):
|
||||
// Transmit PA: CSD=1, CTX=1, CPS=1
|
||||
// Receive LNA: CSD=1, CTX=0, CPS=X (21dB gain, 1.9dB NF)
|
||||
// Receive bypass: CSD=1, CTX=1, CPS=0
|
||||
// Shutdown: CSD=0, CTX=X, CPS=X
|
||||
// Pin mapping:
|
||||
// CPS (pin 5) -> SX1262 DIO2: TX/RX path select (automatic via SX126X_DIO2_AS_RF_SWITCH)
|
||||
// CSD (pin 4) -> GPIO2: Chip enable (HIGH=on, LOW=shutdown)
|
||||
// CTX (pin 6) -> GPIO5: Switch between Receive LNA Mode and Receive Bypass Mode. (HIGH=RX bypass, LOW=RX LNA)
|
||||
// VCC0/VCC1 -> Vfem via U3 LDO, controlled by GPIO7
|
||||
// KCT8103L FEM: TX/RX path switching is handled by DIO2 -> CPS pin (via SX126X_DIO2_AS_RF_SWITCH)
|
||||
|
||||
#define USE_KCT8103L_PA
|
||||
#define LORA_PA_POWER 7 // VFEM_Ctrl - KCT8103L LDO power enable
|
||||
#define LORA_KCT8103L_PA_CSD 2 // CSD - KCT8103L chip enable (HIGH=on)
|
||||
#define LORA_KCT8103L_PA_CTX 5 // CTX - Switch between Receive LNA Mode and Receive Bypass Mode. (HIGH=RX bypass, LOW=RX LNA)
|
||||
|
||||
#if HAS_TFT
|
||||
#define USE_TFTDISPLAY 1
|
||||
#endif
|
||||
/*
|
||||
* GPS pins
|
||||
*/
|
||||
#define GPS_L76K
|
||||
#define PIN_GPS_EN (42)
|
||||
#define GPS_EN_ACTIVE LOW
|
||||
#define PERIPHERAL_WARMUP_MS 1000 // Make sure I2C QuickLink has stable power before continuing
|
||||
#define PIN_GPS_PPS (41)
|
||||
// Seems to be missing on this new board
|
||||
#define GPS_TX_PIN (38) // This is for bits going TOWARDS the CPU
|
||||
#define GPS_RX_PIN (39) // This is for bits going TOWARDS the GPS
|
||||
#define GPS_THREAD_INTERVAL 50
|
||||
@@ -2,7 +2,7 @@
|
||||
[portduino_base]
|
||||
platform =
|
||||
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
|
||||
https://github.com/meshtastic/platform-native/archive/135b91e953db0b5f44d278f8ebd5b8d985fc03d8.zip
|
||||
https://github.com/meshtastic/platform-native/archive/4ea5e09ac7d51a593e12ec7c1ebb6cd06745ce53.zip
|
||||
framework = arduino
|
||||
|
||||
build_src_filter =
|
||||
@@ -37,25 +37,35 @@ lib_deps =
|
||||
# renovate: datasource=github-tags depName=Adafruit_BME680 packageName=adafruit/Adafruit_BME680
|
||||
https://github.com/adafruit/Adafruit_BME680/archive/refs/tags/2.0.6.zip
|
||||
|
||||
build_flags =
|
||||
; Cross-platform build flags shared between native (Linux) and native-macos builds.
|
||||
; Anything Linux-only (libbluetooth, libgpiod, libi2c, /sys/class/gpio,
|
||||
; PORTDUINO_LINUX_HARDWARE, glibc-style _FORTIFY_SOURCE) goes in `build_flags`
|
||||
; instead. UDP multicast also lives there because the firmware's
|
||||
; UdpMulticastHandler.h unconditionally `#include <WiFi.h>` on non-NRF52
|
||||
; targets, which requires the framework's bundled WiFi shim that we
|
||||
; lib_ignore on macOS.
|
||||
build_flags_common =
|
||||
${arduino_base.build_flags}
|
||||
-D ARCH_PORTDUINO
|
||||
-fPIC
|
||||
-D_FORTIFY_SOURCE=2
|
||||
-fstack-protector-all -Wstack-protector --param ssp-buffer-size=4
|
||||
-Isrc/platform/portduino
|
||||
-DRADIOLIB_EEPROM_UNSUPPORTED
|
||||
-DPORTDUINO_LINUX_HARDWARE
|
||||
-DHAS_UDP_MULTICAST=1
|
||||
-lpthread
|
||||
-lstdc++fs
|
||||
-lbluetooth
|
||||
-lgpiod
|
||||
-lyaml-cpp
|
||||
-li2c
|
||||
-luv
|
||||
-std=gnu17
|
||||
-std=gnu++17
|
||||
|
||||
build_flags =
|
||||
${portduino_base.build_flags_common}
|
||||
-DHAS_UDP_MULTICAST=1
|
||||
-D_FORTIFY_SOURCE=2
|
||||
-fstack-protector-all -Wstack-protector --param ssp-buffer-size=4
|
||||
-DPORTDUINO_LINUX_HARDWARE
|
||||
-lstdc++fs
|
||||
-lbluetooth
|
||||
-lgpiod
|
||||
-li2c
|
||||
lib_ignore =
|
||||
Adafruit NeoPixel
|
||||
Adafruit ST7735 and ST7789 Library
|
||||
|
||||
@@ -117,3 +117,92 @@ build_flags = -lgcov --coverage -fprofile-abs-path -fsanitize=address ${env:nati
|
||||
test_testing_command =
|
||||
${platformio.build_dir}/${this.__env__}/meshtasticd
|
||||
-s
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Native build for macOS (Darwin / arm64 + x86_64). Headless meshtasticd that
|
||||
; runs in SimRadio mode (`-s`) or against real LoRa hardware via a CH341
|
||||
; USB-SPI bridge. No BlueZ, libgpiod, or Linux I2C — those require Linux.
|
||||
;
|
||||
; Prerequisites (Homebrew):
|
||||
; brew install platformio yaml-cpp libuv openssl@3 libusb argp-standalone pkg-config
|
||||
;
|
||||
; The macOS-side patches now live upstream:
|
||||
; * meshtastic/platform-native — `String.h`-shadow shim, `-Wno-enum-constexpr-conversion`,
|
||||
; empty-variant-dir guard. Pulled via `portduino_base.platform` zip pin.
|
||||
; * meshtastic/framework-portduino — LinuxHardwareI2C macOS stubs, AsyncUDP
|
||||
; SOCK_NONBLOCK fallback, Common.h __APPLE__ guard, WiFiServer.cpp extern-C
|
||||
; fix, package.json URL refresh. Pulled by platform-native at its pinned commit.
|
||||
; This env therefore only carries the firmware-side build flags and src filter.
|
||||
;
|
||||
; Real LoRa hardware on macOS:
|
||||
; The same lib_dep `pine64/libch341-spi-userspace` used on Linux works on
|
||||
; macOS as-is — its `libusb_detach_kernel_driver()` call is `__linux__`-
|
||||
; guarded, but on macOS the kernel doesn't bind a driver to a CH341A SPI
|
||||
; bridge (PID 0x5512; bDeviceClass=0xff vendor-specific) by default, so
|
||||
; no detach is needed. Apple's bundled CH34x driver targets the CH340
|
||||
; *UART* variant (PID 0x7523) — different product. libusb opens the device
|
||||
; and claims interface 0 directly via IOUSBHostInterface.
|
||||
;
|
||||
; To use, point `meshtasticd` at any of the existing `bin/config.d/lora-*.yaml`
|
||||
; files that specify `spidev: ch341` — they're platform-agnostic. Example:
|
||||
; pio run -e native-macos
|
||||
; mkdir -p ~/.meshtasticd && cp bin/config-dist.yaml ~/.meshtasticd/config.yaml
|
||||
; # Edit ~/.meshtasticd/config.yaml: ConfigDirectory: ./config.d/
|
||||
; mkdir ~/.meshtasticd/config.d && cp bin/config.d/lora-meshstick-1262.yaml ~/.meshtasticd/config.d/
|
||||
; cd ~/.meshtasticd && /path/to/firmware/.pio/build/native-macos/meshtasticd
|
||||
;
|
||||
; The MAC address auto-derives from the CH341's USB serial + product string
|
||||
; (PortduinoGlue.cpp ~497-518); on Linux a BlueZ HCI socket is the fallback
|
||||
; when that path isn't taken, but BlueZ is `__linux__`-guarded so the
|
||||
; serial-derivation path is mandatory on macOS. Override with
|
||||
; `MACAddress: AA:BB:CC:DD:EE:FF` in config.yaml's `General:` section if
|
||||
; the device's serial isn't 8 hex chars.
|
||||
;
|
||||
; Diagnosing CH341 issues on macOS:
|
||||
; ioreg -p IOUSB -l -w 0 | grep -B2 -A30 0x5512
|
||||
; Children should be `IOUSBHostInterface`. If a vendor driver class
|
||||
; (e.g. `com.wch.CH34xVCPDriver` from a third-party WCH installer)
|
||||
; claims interface 0, libusb will fail with LIBUSB_ERROR_BUSY.
|
||||
; Workaround: `sudo kmutil unload -b <bundleID>`.
|
||||
; LIBUSB_DEBUG=4 .pio/build/native-macos/meshtasticd
|
||||
; Verbose libusb trace — useful when claim_interface fails.
|
||||
; ---------------------------------------------------------------------------
|
||||
[env:native-macos]
|
||||
extends = native_base
|
||||
; Apple's ld doesn't accept GNU ld's `-Wl,-Map,<file>` syntax (inherited from
|
||||
; the top-level platformio.ini). Strip it; the linker map isn't useful for
|
||||
; the macOS dev loop anyway, and Apple ld's equivalent (`-Wl,-map,<file>`)
|
||||
; uses different argument shape.
|
||||
build_unflags = -Wl,-Map,"${platformio.build_dir}"/output.map
|
||||
build_flags = ${portduino_base.build_flags_common}
|
||||
-I variants/native/portduino
|
||||
-I/opt/homebrew/include
|
||||
-I/opt/homebrew/opt/argp-standalone/include
|
||||
-I/opt/homebrew/opt/yaml-cpp/include
|
||||
-L/opt/homebrew/lib
|
||||
-L/opt/homebrew/opt/argp-standalone/lib
|
||||
-L/opt/homebrew/opt/yaml-cpp/lib
|
||||
-largp
|
||||
-DPORTDUINO_DARWIN
|
||||
; Headless build — variants/native/portduino/variant.h would otherwise
|
||||
; default HAS_SCREEN to 1 and pull in screen-renderer source that uses
|
||||
; VLA-with-initializer (a GNU/GCC extension Apple Clang rejects).
|
||||
; MESHTASTIC_EXCLUDE_SCREEN gates the optional `screen->setHeading(...)`-
|
||||
; style screen-driver hooks scattered through sensor sources.
|
||||
-DHAS_SCREEN=0
|
||||
-DMESHTASTIC_EXCLUDE_SCREEN=1
|
||||
!pkg-config --libs openssl --silence-errors || :
|
||||
; src/input/Linux*.{cpp,h} drive evdev (`<linux/input.h>`) which doesn't exist
|
||||
; on macOS. graphics/Panel_sdl.* and graphics/TFTDisplay.cpp pull LovyanGFX
|
||||
; (which we lib_ignore on macOS for the <malloc.h> issue). Neither is needed
|
||||
; for the headless build.
|
||||
build_src_filter = ${native_base.build_src_filter}
|
||||
-<input/LinuxInput.cpp>
|
||||
-<input/LinuxInputImpl.cpp>
|
||||
-<graphics/Panel_sdl.cpp>
|
||||
-<graphics/TFTDisplay.cpp>
|
||||
; LovyanGFX includes <malloc.h> (Linux-only) and is only needed by TFT
|
||||
; variants — not relevant for the headless macOS build.
|
||||
lib_ignore =
|
||||
${portduino_base.lib_ignore}
|
||||
LovyanGFX
|
||||
|
||||
Reference in New Issue
Block a user