Merge branch 'master' into store-and-forward

This commit is contained in:
Thomas Göttgens 2024-11-02 16:38:36 +01:00 committed by GitHub
commit 5d25c304ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 570 additions and 388 deletions

View File

@ -245,7 +245,8 @@ jobs:
if: ${{ github.event_name == 'workflow_dispatch' }} if: ${{ github.event_name == 'workflow_dispatch' }}
outputs: outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
needs: [ needs:
[
gather-artifacts, gather-artifacts,
package-raspbian, package-raspbian,
package-raspbian-armv7l, package-raspbian-armv7l,

View File

@ -50,11 +50,14 @@ jobs:
mkdir -p .debpkg/usr/share/doc/meshtasticd/web mkdir -p .debpkg/usr/share/doc/meshtasticd/web
mkdir -p .debpkg/usr/sbin mkdir -p .debpkg/usr/sbin
mkdir -p .debpkg/etc/meshtasticd mkdir -p .debpkg/etc/meshtasticd
mkdir -p .debpkg/etc/meshtasticd/config.d
mkdir -p .debpkg/etc/meshtasticd/available.d
mkdir -p .debpkg/usr/lib/systemd/system/ mkdir -p .debpkg/usr/lib/systemd/system/
tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web
gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz
cp release/meshtasticd_linux_x86_64 .debpkg/usr/sbin/meshtasticd cp release/meshtasticd_linux_x86_64 .debpkg/usr/sbin/meshtasticd
cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml
cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/
chmod +x .debpkg/usr/sbin/meshtasticd chmod +x .debpkg/usr/sbin/meshtasticd
cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service
echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles

View File

@ -50,11 +50,14 @@ jobs:
mkdir -p .debpkg/usr/share/doc/meshtasticd/web mkdir -p .debpkg/usr/share/doc/meshtasticd/web
mkdir -p .debpkg/usr/sbin mkdir -p .debpkg/usr/sbin
mkdir -p .debpkg/etc/meshtasticd mkdir -p .debpkg/etc/meshtasticd
mkdir -p .debpkg/etc/meshtasticd/config.d
mkdir -p .debpkg/etc/meshtasticd/available.d
mkdir -p .debpkg/usr/lib/systemd/system/ mkdir -p .debpkg/usr/lib/systemd/system/
tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web
gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz
cp release/meshtasticd_linux_aarch64 .debpkg/usr/sbin/meshtasticd cp release/meshtasticd_linux_aarch64 .debpkg/usr/sbin/meshtasticd
cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml
cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/
chmod +x .debpkg/usr/sbin/meshtasticd chmod +x .debpkg/usr/sbin/meshtasticd
cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service
echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles

View File

@ -50,11 +50,14 @@ jobs:
mkdir -p .debpkg/usr/share/doc/meshtasticd/web mkdir -p .debpkg/usr/share/doc/meshtasticd/web
mkdir -p .debpkg/usr/sbin mkdir -p .debpkg/usr/sbin
mkdir -p .debpkg/etc/meshtasticd mkdir -p .debpkg/etc/meshtasticd
mkdir -p .debpkg/etc/meshtasticd/config.d
mkdir -p .debpkg/etc/meshtasticd/available.d
mkdir -p .debpkg/usr/lib/systemd/system/ mkdir -p .debpkg/usr/lib/systemd/system/
tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web
gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz
cp release/meshtasticd_linux_armv7l .debpkg/usr/sbin/meshtasticd cp release/meshtasticd_linux_armv7l .debpkg/usr/sbin/meshtasticd
cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml
cp bin/config.d/* .debpkg/etc/meshtasticd/available.d/
chmod +x .debpkg/usr/sbin/meshtasticd chmod +x .debpkg/usr/sbin/meshtasticd
cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service
echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles

21
.github/workflows/stale_bot.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: process stale Issues and PR's
on:
schedule:
- cron: 0 6 * * *
workflow_dispatch: {}
permissions:
issues: write
pull-requests: write
jobs:
stale_issues:
name: Close Stale Issues
runs-on: ubuntu-latest
steps:
- name: Stale PR+Issues
uses: actions/stale@v9.0.0
with:
exempt-issue-labels: pinned,3.0
exempt-pr-labels: pinned,3.0

43
.github/workflows/trunk_format_pr.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: Run Trunk Fmt on PR Comment
on:
issue_comment:
types: [created]
jobs:
trunk-fmt:
if: github.event.issue.pull_request != null && contains(github.event.comment.body, 'trunk fmt')
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Install trunk
run: curl https://get.trunk.io -fsSL | bash
- name: Run Trunk Fmt
run: trunk fmt
- 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 push
- name: Comment on PR
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '`trunk fmt` has been run on this PR.'
})

View File

@ -8,15 +8,15 @@ plugins:
uri: https://github.com/trunk-io/plugins uri: https://github.com/trunk-io/plugins
lint: lint:
enabled: enabled:
- trufflehog@3.82.12 - trufflehog@3.83.2
- yamllint@1.35.1 - yamllint@1.35.1
- bandit@1.7.10 - bandit@1.7.10
- checkov@3.2.256 - checkov@3.2.276
- terrascan@1.19.9 - terrascan@1.19.9
- trivy@0.55.2 - trivy@0.56.2
#- trufflehog@3.63.2-rc0 #- trufflehog@3.63.2-rc0
- taplo@0.9.3 - taplo@0.9.3
- ruff@0.7.0 - ruff@0.7.1
- isort@5.13.2 - isort@5.13.2
- markdownlint@0.42.0 - markdownlint@0.42.0
- oxipng@9.1.2 - oxipng@9.1.2

4
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,4 @@
# Contributor Covenant Code of Conduct
The Meshtastic Firmware project is subject to the code of conduct for the parent project, which can be found here:
https://meshtastic.org/docs/legal/conduct/

View File

@ -31,7 +31,7 @@ build_flags =
-DCONFIG_BT_NIMBLE_ENABLED -DCONFIG_BT_NIMBLE_ENABLED
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2 -DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20 -DCONFIG_BT_NIMBLE_MAX_CCCDS=20
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=5120 -DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=8192
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING -DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
-DSERIAL_BUFFER_SIZE=4096 -DSERIAL_BUFFER_SIZE=4096
-DLIBPAX_ARDUINO -DLIBPAX_ARDUINO

View File

@ -1,15 +1,11 @@
### Many device configs have been moved to /etc/meshtasticd/available.d
### To activate, simply copy or link the appropriate file into /etc/meshtasticd/config.d
### Define your devices here using Broadcom pin numbering ### Define your devices here using Broadcom pin numbering
### Uncomment the block that corresponds to your hardware ### Uncomment the block that corresponds to your hardware
### Including the "Module:" line! ### Including the "Module:" line!
--- ---
Lora: Lora:
# Module: sx1262 # Waveshare SX126X XXXM
# DIO2_AS_RF_SWITCH: true
# CS: 21
# IRQ: 16
# Busy: 20
# Reset: 18
# SX126X_ANT_SW: 6
# Module: sx1262 # Waveshare SX1302 LISTEN ONLY AT THIS TIME! # Module: sx1262 # Waveshare SX1302 LISTEN ONLY AT THIS TIME!
# CS: 7 # CS: 7
@ -179,3 +175,4 @@ Webserver:
General: General:
MaxNodes: 200 MaxNodes: 200
MaxMessageQueue: 100 MaxMessageQueue: 100
ConfigDirectory: /etc/meshtasticd/config.d/

View File

@ -0,0 +1,18 @@
Display:
### Waveshare 2.8inch RPi LCD
Panel: ST7789
CS: 8
DC: 22 # Data/Command pin
Backlight: 18
Width: 240
Height: 320
Reset: 27
Rotate: true
Invert: true
Touchscreen:
### Note, at least for now, the touchscreen must have a CS pin defined, even if you let Linux manage the CS switching.
Module: XPT2046 # Waveshare 2.8inch
CS: 7
IRQ: 17

View File

@ -0,0 +1,8 @@
Lora:
Module: sx1262 # Waveshare SX126X XXXM
DIO2_AS_RF_SWITCH: true
CS: 21
IRQ: 16
Busy: 20
Reset: 18
SX126X_ANT_SW: 6

@ -1 +1 @@
Subproject commit 7dc7a6d21991efe362c7cbb556573f6bb285e407 Subproject commit 015202aead5f6807d63537c58f4cb6525f19e56f

View File

@ -154,9 +154,16 @@ static void adcEnable()
#ifdef ADC_CTRL // enable adc voltage divider when we need to read #ifdef ADC_CTRL // enable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP #ifdef ADC_USE_PULLUP
pinMode(ADC_CTRL, INPUT_PULLUP); pinMode(ADC_CTRL, INPUT_PULLUP);
#else
#ifdef HELTEC_V3
pinMode(ADC_CTRL, INPUT);
uint8_t adc_ctl_enable_value = !(digitalRead(ADC_CTRL));
pinMode(ADC_CTRL, OUTPUT);
digitalWrite(ADC_CTRL, adc_ctl_enable_value);
#else #else
pinMode(ADC_CTRL, OUTPUT); pinMode(ADC_CTRL, OUTPUT);
digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED); digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED);
#endif
#endif #endif
delay(10); delay(10);
#endif #endif
@ -167,10 +174,14 @@ static void adcDisable()
#ifdef ADC_CTRL // disable adc voltage divider when we need to read #ifdef ADC_CTRL // disable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP #ifdef ADC_USE_PULLUP
pinMode(ADC_CTRL, INPUT_PULLDOWN); pinMode(ADC_CTRL, INPUT_PULLDOWN);
#else
#ifdef HELTEC_V3
pinMode(ADC_CTRL, ANALOG);
#else #else
digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED); digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED);
#endif #endif
#endif #endif
#endif
} }
#endif #endif

View File

@ -207,6 +207,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef GPS_BAUDRATE #ifndef GPS_BAUDRATE
#define GPS_BAUDRATE 9600 #define GPS_BAUDRATE 9600
#define GPS_BAUDRATE_FIXED 0
#else
#define GPS_BAUDRATE_FIXED 1
#endif #endif
/* Step #2: follow with defines common to the architecture; /* Step #2: follow with defines common to the architecture;

View File

@ -413,30 +413,42 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
return 0; return 0;
} }
/**
* @brief Setup the GPS based on the model detected.
* We detect the GPS by cycling through a set of baud rates, first common then rare.
* For each baud rate, we run GPS::Probe to send commands and match the responses
* to known GPS responses.
* @retval Whether setup reached the end of its potential to configure the GPS.
*/
bool GPS::setup() bool GPS::setup()
{ {
if (!didSerialInit) { if (!didSerialInit) {
int msglen = 0; int msglen = 0;
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) { if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
if (probeTries < 2) {
// if GPS_BAUDRATE is specified in variant (i.e. not 9600), skip to the specified rate. LOG_DEBUG("Probing for GPS at %d", serialSpeeds[speedSelect]);
if (speedSelect == 0 && GPS_BAUDRATE != serialSpeeds[speedSelect]) { gnssModel = probe(serialSpeeds[speedSelect]);
speedSelect = std::find(serialSpeeds, std::end(serialSpeeds), GPS_BAUDRATE) - serialSpeeds; if (gnssModel == GNSS_MODEL_UNKNOWN) {
if (++speedSelect == sizeof(serialSpeeds) / sizeof(int)) {
speedSelect = 0;
++probeTries;
}
}
} }
// Rare Serial Speeds
LOG_DEBUG("Probing for GPS at %d", serialSpeeds[speedSelect]); if (probeTries == 2) {
gnssModel = probe(serialSpeeds[speedSelect]); LOG_DEBUG("Probing for GPS at %d", rareSerialSpeeds[speedSelect]);
if (gnssModel == GNSS_MODEL_UNKNOWN) { gnssModel = probe(rareSerialSpeeds[speedSelect]);
if (++speedSelect == sizeof(serialSpeeds) / sizeof(int)) { if (gnssModel == GNSS_MODEL_UNKNOWN) {
speedSelect = 0; if (++speedSelect == sizeof(rareSerialSpeeds) / sizeof(int)) {
if (--probeTries == 0) { LOG_WARN("Giving up on GPS probe and setting to %d", GPS_BAUDRATE);
LOG_WARN("Giving up on GPS probe and setting to 9600.");
return true; return true;
} }
} }
return false;
} }
return false;
} else { } else {
gnssModel = GNSS_MODEL_UNKNOWN; gnssModel = GNSS_MODEL_UNKNOWN;
} }
@ -476,6 +488,18 @@ bool GPS::setup()
// Switch to Fitness Mode, for running and walking purpose with low speed (<5 m/s) // Switch to Fitness Mode, for running and walking purpose with low speed (<5 m/s)
_serial_gps->write("$PMTK886,1*29\r\n"); _serial_gps->write("$PMTK886,1*29\r\n");
delay(250); delay(250);
} else if (gnssModel == GNSS_MODEL_MTK_PA1616S) {
// PA1616S is used in some GPS breakout boards from Adafruit
// PA1616S does not have GLONASS capability. PA1616D does, but is not implemented here.
_serial_gps->write("$PMTK353,1,0,0,0,0*2A\r\n");
// Above command will reset the GPS and takes longer before it will accept new commands
delay(1000);
// Only ask for RMC and GGA (GNRMC and GNGGA)
_serial_gps->write("$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n");
delay(250);
// Enable SBAS / WAAS
_serial_gps->write("$PMTK301,2*2E\r\n");
delay(250);
} else if (gnssModel == GNSS_MODEL_ATGM336H) { } else if (gnssModel == GNSS_MODEL_ATGM336H) {
// Set the intial configuration of the device - these _should_ work for most AT6558 devices // Set the intial configuration of the device - these _should_ work for most AT6558 devices
msglen = makeCASPacket(0x06, 0x07, sizeof(_message_CAS_CFG_NAVX_CONF), _message_CAS_CFG_NAVX_CONF); msglen = makeCASPacket(0x06, 0x07, sizeof(_message_CAS_CFG_NAVX_CONF), _message_CAS_CFG_NAVX_CONF);
@ -663,7 +687,8 @@ bool GPS::setup()
SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_SBAS_BBR, "disable SBAS M10 GPS BBR", 300); SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_DISABLE_SBAS_BBR, "disable SBAS M10 GPS BBR", 300);
delay(750); // will cause a receiver restart so wait a bit delay(750); // will cause a receiver restart so wait a bit
// Done with initialization, Now enable wanted NMEA messages in BBR layer so they will survive a periodic sleep. // Done with initialization, Now enable wanted NMEA messages in BBR layer so they will survive a periodic
// sleep.
SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ENABLE_NMEA_BBR, "enable messages for M10 GPS BBR", 300); SEND_UBX_PACKET(0x06, 0x8A, _message_VALSET_ENABLE_NMEA_BBR, "enable messages for M10 GPS BBR", 300);
delay(750); delay(750);
// Next enable wanted NMEA messages in RAM layer // Next enable wanted NMEA messages in RAM layer
@ -925,10 +950,10 @@ void GPS::down()
#endif #endif
if (softsleepSupported) { if (softsleepSupported) {
// How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP? // How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than
// Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050 // GPS_SOFTSLEEP? Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M
// https://www.desmos.com/calculator/6gvjghoumr // and M10050 https://www.desmos.com/calculator/6gvjghoumr This is not particularly accurate, but probably an
// This is not particularly accurate, but probably an impromevement over a single, fixed threshold // improvement over a single, fixed threshold
uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22)); uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22));
LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep", hardsleepThreshold / 1000); LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep", hardsleepThreshold / 1000);
@ -1144,6 +1169,7 @@ GnssModel_t GPS::probe(int serialSpeed)
delay(20); delay(20);
PROBE_SIMPLE("L76B", "$PMTK605*31", "Quectel-L76B", GNSS_MODEL_MTK_L76B, 500); PROBE_SIMPLE("L76B", "$PMTK605*31", "Quectel-L76B", GNSS_MODEL_MTK_L76B, 500);
PROBE_SIMPLE("PA1616S", "$PMTK605*31", "1616S", GNSS_MODEL_MTK_PA1616S, 500);
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00}; uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
UBXChecksum(cfg_rate, sizeof(cfg_rate)); UBXChecksum(cfg_rate, sizeof(cfg_rate));
@ -1280,10 +1306,12 @@ GPS *GPS::createGps()
if (!GPS_EN_ACTIVE) { // Need to invert the pin before hardware if (!GPS_EN_ACTIVE) { // Need to invert the pin before hardware
new GpioNotTransformer( new GpioNotTransformer(
virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio virtPin,
p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio
} else { } else {
new GpioUnaryTransformer( new GpioUnaryTransformer(
virtPin, p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio virtPin,
p); // We just leave this created object on the heap so it can stay watching virtPin and driving en_gpio
} }
} }
@ -1391,8 +1419,8 @@ bool GPS::factoryReset()
_serial_gps->write("$PMTK104*37\r\n"); _serial_gps->write("$PMTK104*37\r\n");
// No PMTK_ACK for this command. // No PMTK_ACK for this command.
delay(100); delay(100);
// send the UBLOX Factory Reset Command regardless of detect state, something is very wrong, just assume it's UBLOX. // send the UBLOX Factory Reset Command regardless of detect state, something is very wrong, just assume it's
// Factory Reset // UBLOX. Factory Reset
byte _message_reset[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFB, 0x00, 0x00, 0x00, byte _message_reset[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFB, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x17, 0x2B, 0x7E}; 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x17, 0x2B, 0x7E};
_serial_gps->write(_message_reset, sizeof(_message_reset)); _serial_gps->write(_message_reset, sizeof(_message_reset));
@ -1431,8 +1459,8 @@ bool GPS::lookForTime()
auto d = reader.date; auto d = reader.date;
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
/* Convert to unix time /* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970 The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1,
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z). 1970 (midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
*/ */
struct tm t; struct tm t;
t.tm_sec = ti.second() + round(ti.age() / 1000); t.tm_sec = ti.second() + round(ti.age() / 1000);
@ -1665,7 +1693,9 @@ bool GPS::whileActive()
} }
} }
#ifdef GPS_DEBUG #ifdef GPS_DEBUG
LOG_DEBUG(debugmsg.c_str()); if (debugmsg != "") {
LOG_DEBUG(debugmsg.c_str());
}
#endif #endif
return isValid; return isValid;
} }

View File

@ -34,6 +34,7 @@ typedef enum {
GNSS_MODEL_UC6580, GNSS_MODEL_UC6580,
GNSS_MODEL_UNKNOWN, GNSS_MODEL_UNKNOWN,
GNSS_MODEL_MTK_L76B, GNSS_MODEL_MTK_L76B,
GNSS_MODEL_MTK_PA1616S,
GNSS_MODEL_AG3335, GNSS_MODEL_AG3335,
GNSS_MODEL_AG3352 GNSS_MODEL_AG3352
} GnssModel_t; } GnssModel_t;
@ -75,13 +76,21 @@ class GPS : private concurrency::OSThread
uint8_t fixType = 0; // fix type from GPGSA uint8_t fixType = 0; // fix type from GPGSA
#endif #endif
private: private:
const int serialSpeeds[6] = {9600, 115200, 38400, 4800, 57600, 9600}; #if GPS_BAUDRATE_FIXED
// if GPS_BAUDRATE is specified in variant, only try that.
const int serialSpeeds[1] = {GPS_BAUDRATE};
const int rareSerialSpeeds[1] = {GPS_BAUDRATE};
#else
const int serialSpeeds[3] = {9600, 115200, 38400};
const int rareSerialSpeeds[3] = {4800, 57600, GPS_BAUDRATE};
#endif
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0; uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
uint32_t rx_gpio = 0; uint32_t rx_gpio = 0;
uint32_t tx_gpio = 0; uint32_t tx_gpio = 0;
int speedSelect = 0; int speedSelect = 0;
int probeTries = 2; int probeTries = 0;
/** /**
* hasValidLocation - indicates that the position variables contain a complete * hasValidLocation - indicates that the position variables contain a complete

View File

@ -70,9 +70,9 @@ bool GPSUpdateScheduling::isUpdateDue()
// Have we been searching for a GPS position for too long? // Have we been searching for a GPS position for too long?
bool GPSUpdateScheduling::searchedTooLong() bool GPSUpdateScheduling::searchedTooLong()
{ {
uint32_t maxSearchMs = uint32_t minimumOrConfiguredSecs =
Default::getConfiguredOrDefaultMs(config.position.position_broadcast_secs, default_broadcast_interval_secs); 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 broadcast interval set to max, no such thing as "too long"
if (maxSearchMs == UINT32_MAX) if (maxSearchMs == UINT32_MAX)
return false; return false;
@ -115,4 +115,4 @@ void GPSUpdateScheduling::updateLockTimePrediction()
uint32_t GPSUpdateScheduling::predictedSearchDurationMs() uint32_t GPSUpdateScheduling::predictedSearchDurationMs()
{ {
return GPSUpdateScheduling::predictedMsToGetLock; return GPSUpdateScheduling::predictedMsToGetLock;
} }

View File

@ -59,7 +59,7 @@ int32_t ScanAndSelectInput::runOnce()
// If: "no messages added" alert screen currently shown // If: "no messages added" alert screen currently shown
if (alertingNoMessage) { if (alertingNoMessage) {
// Dismiss the alert screen several seconds after it appears // Dismiss the alert screen several seconds after it appears
if (now > alertingSinceMs + durationAlertMs) { if (!Throttle::isWithinTimespanMs(alertingSinceMs, durationAlertMs)) {
alertingNoMessage = false; alertingNoMessage = false;
screen->endAlert(); screen->endAlert();
} }
@ -74,9 +74,9 @@ int32_t ScanAndSelectInput::runOnce()
// Existing press // Existing press
else { else {
// Duration enough for long press // Longer than shortpress window
// Long press not yet fired (prevent repeat firing while held) // Long press not yet fired (prevent repeat firing while held)
if (!longPressFired && Throttle::isWithinTimespanMs(downSinceMs, durationLongMs)) { if (!longPressFired && !Throttle::isWithinTimespanMs(downSinceMs, durationLongMs)) {
longPressFired = true; longPressFired = true;
longPress(); longPress();
} }
@ -91,7 +91,9 @@ int32_t ScanAndSelectInput::runOnce()
// Button newly released // Button newly released
// Long press event didn't already fire // Long press event didn't already fire
if (held && !longPressFired) { if (held && !longPressFired) {
// Duration enough for short press // Duration within shortpress window
// - longer than durationShortPress (debounce)
// - shorter than durationLongPress
if (!Throttle::isWithinTimespanMs(downSinceMs, durationShortMs)) { if (!Throttle::isWithinTimespanMs(downSinceMs, durationShortMs)) {
shortPress(); shortPress();
} }

View File

@ -140,11 +140,12 @@ NodeDB::NodeDB()
memcpy(myNodeInfo.device_id.bytes, &device_id_start, sizeof(device_id_start)); memcpy(myNodeInfo.device_id.bytes, &device_id_start, sizeof(device_id_start));
memcpy(myNodeInfo.device_id.bytes + sizeof(device_id_start), &device_id_end, sizeof(device_id_end)); memcpy(myNodeInfo.device_id.bytes + sizeof(device_id_start), &device_id_end, sizeof(device_id_end));
myNodeInfo.device_id.size = 16; myNodeInfo.device_id.size = 16;
hasUniqueId = true; // Uncomment below to print the device id
// hasUniqueId = true;
#else #else
// FIXME - implement for other platforms // FIXME - implement for other platforms
#endif #endif
// Uncomment below to print the device id
// if (hasUniqueId) { // if (hasUniqueId) {
// std::string deviceIdHex; // std::string deviceIdHex;
// for (size_t i = 0; i < myNodeInfo.device_id.size; ++i) { // for (size_t i = 0; i < myNodeInfo.device_id.size; ++i) {
@ -177,21 +178,23 @@ NodeDB::NodeDB()
} }
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI) #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
bool keygenSuccess = false; if (!owner.is_licensed) {
if (config.security.private_key.size == 32) { bool keygenSuccess = false;
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) { if (config.security.private_key.size == 32) {
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
keygenSuccess = true;
}
} else {
LOG_INFO("Generating new PKI keys");
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
keygenSuccess = true; keygenSuccess = true;
} }
} else { if (keygenSuccess) {
LOG_INFO("Generating new PKI keys"); config.security.public_key.size = 32;
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes); config.security.private_key.size = 32;
keygenSuccess = true; owner.public_key.size = 32;
} memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
if (keygenSuccess) { }
config.security.public_key.size = 32;
config.security.private_key.size = 32;
owner.public_key.size = 32;
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
} }
#elif !(MESHTASTIC_EXCLUDE_PKI) #elif !(MESHTASTIC_EXCLUDE_PKI)
// Calculate Curve25519 public and private keys // Calculate Curve25519 public and private keys
@ -560,8 +563,10 @@ void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
if (role == meshtastic_Config_DeviceConfig_Role_ROUTER) { if (role == meshtastic_Config_DeviceConfig_Role_ROUTER) {
initConfigIntervals(); initConfigIntervals();
initModuleConfigIntervals(); initModuleConfigIntervals();
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY;
} else if (role == meshtastic_Config_DeviceConfig_Role_REPEATER) { } else if (role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
config.display.screen_on_secs = 1; config.display.screen_on_secs = 1;
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY;
} else if (role == meshtastic_Config_DeviceConfig_Role_SENSOR) { } else if (role == meshtastic_Config_DeviceConfig_Role_SENSOR) {
moduleConfig.telemetry.environment_measurement_enabled = true; moduleConfig.telemetry.environment_measurement_enabled = true;
moduleConfig.telemetry.environment_update_interval = 300; moduleConfig.telemetry.environment_update_interval = 300;

View File

@ -271,7 +271,7 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) { config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
delay = random(0, 2 * CWsize) * slotTimeMsec; delay = random(0, 2 * CWsize) * slotTimeMsec;
LOG_DEBUG("rx_snr found in packet. As a router, setting tx delay:%d", delay); LOG_DEBUG("rx_snr found in packet. Router: setting tx delay:%d", delay);
} else { } else {
// offset the maximum delay for routers: (2 * CWmax * slotTimeMsec) // offset the maximum delay for routers: (2 * CWmax * slotTimeMsec)
delay = (2 * CWmax * slotTimeMsec) + random(0, pow(2, CWsize)) * slotTimeMsec; delay = (2 * CWmax * slotTimeMsec) + random(0, pow(2, CWsize)) * slotTimeMsec;

View File

@ -11,6 +11,10 @@
#include <pb_decode.h> #include <pb_decode.h>
#include <pb_encode.h> #include <pb_encode.h>
#if ARCH_PORTDUINO
#include "PortduinoGlue.h"
#include "meshUtils.h"
#endif
void LockingArduinoHal::spiBeginTransaction() void LockingArduinoHal::spiBeginTransaction()
{ {
spiLock->lock(); spiLock->lock();
@ -278,11 +282,14 @@ void RadioLibInterface::onNotify(uint32_t notification)
// Send any outgoing packets we have ready // Send any outgoing packets we have ready
meshtastic_MeshPacket *txp = txQueue.dequeue(); meshtastic_MeshPacket *txp = txQueue.dequeue();
assert(txp); assert(txp);
bool isLoraTx = txp->to != NODENUM_BROADCAST_NO_LORA;
startSend(txp); startSend(txp);
// Packet has been sent, count it toward our TX airtime utilization. if (isLoraTx) {
uint32_t xmitMsec = getPacketTime(txp); // Packet has been sent, count it toward our TX airtime utilization.
airTime->logAirtime(TX_LOG, xmitMsec); uint32_t xmitMsec = getPacketTime(txp);
airTime->logAirtime(TX_LOG, xmitMsec);
}
} }
} }
} else { } else {
@ -390,6 +397,11 @@ void RadioLibInterface::handleReceiveInterrupt()
#endif #endif
int state = iface->readData((uint8_t *)&radioBuffer, length); int state = iface->readData((uint8_t *)&radioBuffer, length);
#if ARCH_PORTDUINO
if (settingsMap[logoutputlevel] == level_trace) {
printBytes("Raw incoming packet: ", (uint8_t *)&radioBuffer, length);
}
#endif
if (state != RADIOLIB_ERR_NONE) { if (state != RADIOLIB_ERR_NONE) {
LOG_ERROR("ignoring received packet due to error=%d", state); LOG_ERROR("ignoring received packet due to error=%d", state);
rxBad++; rxBad++;

View File

@ -81,14 +81,17 @@ int32_t Router::runOnce()
*/ */
void Router::enqueueReceivedMessage(meshtastic_MeshPacket *p) void Router::enqueueReceivedMessage(meshtastic_MeshPacket *p)
{ {
if (fromRadioQueue.enqueue(p, 0)) { // NOWAIT - fixme, if queue is full, delete older messages // Try enqueue until successful
while (!fromRadioQueue.enqueue(p, 0)) {
// Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME meshtastic_MeshPacket *old_p;
setReceivedMessage(); old_p = fromRadioQueue.dequeuePtr(0); // Dequeue and discard the oldest packet
} else { if (old_p) {
printPacket("BUG! fromRadioQueue is full! Discarding!", p); printPacket("fromRadioQ full, drop oldest!", old_p);
packetPool.release(p); packetPool.release(old_p);
}
} }
// Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME
setReceivedMessage();
} }
/// Generate a unique packet id /// Generate a unique packet id
@ -591,19 +594,20 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
skipHandle = true; skipHandle = true;
} }
bool shouldIgnoreNonstandardPorts =
config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY;
#if USERPREFS_EVENT_MODE #if USERPREFS_EVENT_MODE
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && shouldIgnoreNonstandardPorts = true;
(p->decoded.portnum == meshtastic_PortNum_ATAK_FORWARDER || p->decoded.portnum == meshtastic_PortNum_ATAK_PLUGIN || #endif
p->decoded.portnum == meshtastic_PortNum_PAXCOUNTER_APP || p->decoded.portnum == meshtastic_PortNum_IP_TUNNEL_APP || if (shouldIgnoreNonstandardPorts && p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
p->decoded.portnum == meshtastic_PortNum_AUDIO_APP || p->decoded.portnum == meshtastic_PortNum_PRIVATE_APP || IS_ONE_OF(p->decoded.portnum, meshtastic_PortNum_ATAK_FORWARDER, meshtastic_PortNum_ATAK_PLUGIN,
p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP || meshtastic_PortNum_PAXCOUNTER_APP, meshtastic_PortNum_IP_TUNNEL_APP, meshtastic_PortNum_AUDIO_APP,
p->decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP || meshtastic_PortNum_PRIVATE_APP, meshtastic_PortNum_DETECTION_SENSOR_APP, meshtastic_PortNum_RANGE_TEST_APP,
p->decoded.portnum == meshtastic_PortNum_REMOTE_HARDWARE_APP)) { meshtastic_PortNum_REMOTE_HARDWARE_APP)) {
LOG_DEBUG("Ignoring packet on blacklisted portnum during event"); LOG_DEBUG("Ignoring packet on blacklisted portnum for CORE_PORTNUMS_ONLY");
cancelSending(p->from, p->id); cancelSending(p->from, p->id);
skipHandle = true; skipHandle = true;
} }
#endif
} else { } else {
printPacket("packet decoding failed or skipped (no PSK?)", p); printPacket("packet decoding failed or skipped (no PSK?)", p);
} }

View File

@ -40,7 +40,19 @@ typedef enum _meshtastic_Language {
/* Polish */ /* Polish */
meshtastic_Language_POLISH = 8, meshtastic_Language_POLISH = 8,
/* Turkish */ /* Turkish */
meshtastic_Language_TURKISH = 9 meshtastic_Language_TURKISH = 9,
/* Serbian */
meshtastic_Language_SERBIAN = 10,
/* Russian */
meshtastic_Language_RUSSIAN = 11,
/* Dutch */
meshtastic_Language_DUTCH = 12,
/* Greek */
meshtastic_Language_GREEK = 13,
/* Simplified Chinese (experimental) */
meshtastic_Language_SIMPLIFIED_CHINESE = 30,
/* Traditional Chinese (experimental) */
meshtastic_Language_TRADITIONAL_CHINESE = 31
} meshtastic_Language; } meshtastic_Language;
/* Struct definitions */ /* Struct definitions */
@ -73,16 +85,22 @@ typedef struct _meshtastic_NodeHighlight {
} meshtastic_NodeHighlight; } meshtastic_NodeHighlight;
typedef struct _meshtastic_DeviceUIConfig { typedef struct _meshtastic_DeviceUIConfig {
/* A version integer used to invalidate saved files when we make incompatible changes. */
uint32_t version;
/* TFT display brightness 1..255 */ /* TFT display brightness 1..255 */
uint8_t screen_brightness; uint8_t screen_brightness;
/* Screen timeout 0..900 */ /* Screen timeout 0..900 */
uint16_t screen_timeout; uint16_t screen_timeout;
/* Screen lock enabled */ /* Screen/Settings lock enabled */
bool screen_lock; bool screen_lock;
bool settings_lock;
uint32_t pin_code;
/* Color theme */ /* Color theme */
meshtastic_Theme theme; meshtastic_Theme theme;
/* Audible message alert enabled */ /* Audible message, banner and ring tone */
bool alert_enabled; bool alert_enabled;
bool banner_enabled;
uint8_t ring_tone_id;
/* Localization */ /* Localization */
meshtastic_Language language; meshtastic_Language language;
/* Node list filter */ /* Node list filter */
@ -104,8 +122,8 @@ extern "C" {
#define _meshtastic_Theme_ARRAYSIZE ((meshtastic_Theme)(meshtastic_Theme_RED+1)) #define _meshtastic_Theme_ARRAYSIZE ((meshtastic_Theme)(meshtastic_Theme_RED+1))
#define _meshtastic_Language_MIN meshtastic_Language_ENGLISH #define _meshtastic_Language_MIN meshtastic_Language_ENGLISH
#define _meshtastic_Language_MAX meshtastic_Language_TURKISH #define _meshtastic_Language_MAX meshtastic_Language_TRADITIONAL_CHINESE
#define _meshtastic_Language_ARRAYSIZE ((meshtastic_Language)(meshtastic_Language_TURKISH+1)) #define _meshtastic_Language_ARRAYSIZE ((meshtastic_Language)(meshtastic_Language_TRADITIONAL_CHINESE+1))
#define meshtastic_DeviceUIConfig_theme_ENUMTYPE meshtastic_Theme #define meshtastic_DeviceUIConfig_theme_ENUMTYPE meshtastic_Theme
#define meshtastic_DeviceUIConfig_language_ENUMTYPE meshtastic_Language #define meshtastic_DeviceUIConfig_language_ENUMTYPE meshtastic_Language
@ -114,10 +132,10 @@ extern "C" {
/* Initializer values for message structs */ /* Initializer values for message structs */
#define meshtastic_DeviceUIConfig_init_default {0, 0, 0, _meshtastic_Theme_MIN, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_default, false, meshtastic_NodeHighlight_init_default} #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}
#define meshtastic_NodeFilter_init_default {0, 0, 0, 0, 0, ""} #define meshtastic_NodeFilter_init_default {0, 0, 0, 0, 0, ""}
#define meshtastic_NodeHighlight_init_default {0, 0, 0, 0, ""} #define meshtastic_NodeHighlight_init_default {0, 0, 0, 0, ""}
#define meshtastic_DeviceUIConfig_init_zero {0, 0, 0, _meshtastic_Theme_MIN, 0, _meshtastic_Language_MIN, false, meshtastic_NodeFilter_init_zero, false, meshtastic_NodeHighlight_init_zero} #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}
#define meshtastic_NodeFilter_init_zero {0, 0, 0, 0, 0, ""} #define meshtastic_NodeFilter_init_zero {0, 0, 0, 0, 0, ""}
#define meshtastic_NodeHighlight_init_zero {0, 0, 0, 0, ""} #define meshtastic_NodeHighlight_init_zero {0, 0, 0, 0, ""}
@ -133,25 +151,35 @@ extern "C" {
#define meshtastic_NodeHighlight_telemetry_switch_tag 3 #define meshtastic_NodeHighlight_telemetry_switch_tag 3
#define meshtastic_NodeHighlight_iaq_switch_tag 4 #define meshtastic_NodeHighlight_iaq_switch_tag 4
#define meshtastic_NodeHighlight_node_name_tag 5 #define meshtastic_NodeHighlight_node_name_tag 5
#define meshtastic_DeviceUIConfig_screen_brightness_tag 1 #define meshtastic_DeviceUIConfig_version_tag 1
#define meshtastic_DeviceUIConfig_screen_timeout_tag 2 #define meshtastic_DeviceUIConfig_screen_brightness_tag 2
#define meshtastic_DeviceUIConfig_screen_lock_tag 3 #define meshtastic_DeviceUIConfig_screen_timeout_tag 3
#define meshtastic_DeviceUIConfig_theme_tag 4 #define meshtastic_DeviceUIConfig_screen_lock_tag 4
#define meshtastic_DeviceUIConfig_alert_enabled_tag 5 #define meshtastic_DeviceUIConfig_settings_lock_tag 5
#define meshtastic_DeviceUIConfig_language_tag 6 #define meshtastic_DeviceUIConfig_pin_code_tag 6
#define meshtastic_DeviceUIConfig_node_filter_tag 7 #define meshtastic_DeviceUIConfig_theme_tag 7
#define meshtastic_DeviceUIConfig_node_highlight_tag 8 #define meshtastic_DeviceUIConfig_alert_enabled_tag 8
#define meshtastic_DeviceUIConfig_banner_enabled_tag 9
#define meshtastic_DeviceUIConfig_ring_tone_id_tag 10
#define meshtastic_DeviceUIConfig_language_tag 11
#define meshtastic_DeviceUIConfig_node_filter_tag 12
#define meshtastic_DeviceUIConfig_node_highlight_tag 13
/* Struct field encoding specification for nanopb */ /* Struct field encoding specification for nanopb */
#define meshtastic_DeviceUIConfig_FIELDLIST(X, a) \ #define meshtastic_DeviceUIConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, screen_brightness, 1) \ X(a, STATIC, SINGULAR, UINT32, version, 1) \
X(a, STATIC, SINGULAR, UINT32, screen_timeout, 2) \ X(a, STATIC, SINGULAR, UINT32, screen_brightness, 2) \
X(a, STATIC, SINGULAR, BOOL, screen_lock, 3) \ X(a, STATIC, SINGULAR, UINT32, screen_timeout, 3) \
X(a, STATIC, SINGULAR, UENUM, theme, 4) \ X(a, STATIC, SINGULAR, BOOL, screen_lock, 4) \
X(a, STATIC, SINGULAR, BOOL, alert_enabled, 5) \ X(a, STATIC, SINGULAR, BOOL, settings_lock, 5) \
X(a, STATIC, SINGULAR, UENUM, language, 6) \ X(a, STATIC, SINGULAR, UINT32, pin_code, 6) \
X(a, STATIC, OPTIONAL, MESSAGE, node_filter, 7) \ X(a, STATIC, SINGULAR, UENUM, theme, 7) \
X(a, STATIC, OPTIONAL, MESSAGE, node_highlight, 8) X(a, STATIC, SINGULAR, BOOL, alert_enabled, 8) \
X(a, STATIC, SINGULAR, BOOL, banner_enabled, 9) \
X(a, STATIC, SINGULAR, UINT32, ring_tone_id, 10) \
X(a, STATIC, SINGULAR, UENUM, language, 11) \
X(a, STATIC, OPTIONAL, MESSAGE, node_filter, 12) \
X(a, STATIC, OPTIONAL, MESSAGE, node_highlight, 13)
#define meshtastic_DeviceUIConfig_CALLBACK NULL #define meshtastic_DeviceUIConfig_CALLBACK NULL
#define meshtastic_DeviceUIConfig_DEFAULT NULL #define meshtastic_DeviceUIConfig_DEFAULT NULL
#define meshtastic_DeviceUIConfig_node_filter_MSGTYPE meshtastic_NodeFilter #define meshtastic_DeviceUIConfig_node_filter_MSGTYPE meshtastic_NodeFilter
@ -187,7 +215,7 @@ extern const pb_msgdesc_t meshtastic_NodeHighlight_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_DEVICE_UI_PB_H_MAX_SIZE meshtastic_DeviceUIConfig_size #define MESHTASTIC_MESHTASTIC_DEVICE_UI_PB_H_MAX_SIZE meshtastic_DeviceUIConfig_size
#define meshtastic_DeviceUIConfig_size 80 #define meshtastic_DeviceUIConfig_size 99
#define meshtastic_NodeFilter_size 36 #define meshtastic_NodeFilter_size 36
#define meshtastic_NodeHighlight_size 25 #define meshtastic_NodeHighlight_size 25

View File

@ -408,7 +408,7 @@ typedef enum _meshtastic_LogRecord_Level {
} meshtastic_LogRecord_Level; } meshtastic_LogRecord_Level;
/* Struct definitions */ /* Struct definitions */
/* a gps position */ /* A GPS Position */
typedef struct _meshtastic_Position { typedef struct _meshtastic_Position {
/* The new preferred location encoding, multiply by 1e-7 to get degrees /* The new preferred location encoding, multiply by 1e-7 to get degrees
in floating point */ in floating point */

View File

@ -3,7 +3,6 @@
#include "FSCommon.h" #include "FSCommon.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include <Arduino.h> #include <Arduino.h>
#include <assert.h>
#include <pb_decode.h> #include <pb_decode.h>
#include <pb_encode.h> #include <pb_encode.h>
@ -14,8 +13,6 @@ size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc
pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize); pb_ostream_t stream = pb_ostream_from_buffer(destbuf, destbufsize);
if (!pb_encode(&stream, fields, src_struct)) { if (!pb_encode(&stream, fields, src_struct)) {
LOG_ERROR("Panic: can't encode protobuf reason='%s'", PB_GET_ERROR(&stream)); LOG_ERROR("Panic: can't encode protobuf reason='%s'", PB_GET_ERROR(&stream));
assert(
0); // If this assert fails it probably means you made a field too large for the max limits specified in mesh.options
return 0; return 0;
} else { } else {
return stream.bytes_written; return stream.bytes_written;
@ -71,4 +68,4 @@ bool is_in_helper(uint32_t n, const uint32_t *array, pb_size_t count)
return true; return true;
return false; return false;
} }

View File

@ -61,15 +61,13 @@ char *strnstr(const char *s, const char *find, size_t slen)
void printBytes(const char *label, const uint8_t *p, size_t numbytes) void printBytes(const char *label, const uint8_t *p, size_t numbytes)
{ {
int labelSize = strlen(label); int labelSize = strlen(label);
if (labelSize < 100 && numbytes < 64) { char *messageBuffer = new char[labelSize + (numbytes * 3) + 2];
char *messageBuffer = new char[labelSize + (numbytes * 3) + 2]; strncpy(messageBuffer, label, labelSize);
strncpy(messageBuffer, label, labelSize); for (size_t i = 0; i < numbytes; i++)
for (size_t i = 0; i < numbytes; i++) snprintf(messageBuffer + labelSize + i * 3, 4, " %02x", p[i]);
snprintf(messageBuffer + labelSize + i * 3, 4, " %02x", p[i]); strcpy(messageBuffer + labelSize + numbytes * 3, "\n");
strcpy(messageBuffer + labelSize + numbytes * 3, "\n"); LOG_DEBUG(messageBuffer);
LOG_DEBUG(messageBuffer); delete[] messageBuffer;
delete[] messageBuffer;
}
} }
bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes) bool memfll(const uint8_t *mem, uint8_t find, size_t numbytes)

View File

@ -93,7 +93,7 @@ int32_t ExternalNotificationModule::runOnce()
nagCycleCutoff = UINT32_MAX; nagCycleCutoff = UINT32_MAX;
LOG_INFO("Turning off external notification: "); LOG_INFO("Turning off external notification: ");
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
setExternalOff(i); setExternalState(i, false);
externalTurnedOn[i] = 0; externalTurnedOn[i] = 0;
LOG_INFO("%d ", i); LOG_INFO("%d ", i);
} }
@ -114,17 +114,17 @@ int32_t ExternalNotificationModule::runOnce()
if (externalTurnedOn[0] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms if (externalTurnedOn[0] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms
: EXT_NOTIFICATION_MODULE_OUTPUT_MS) < : EXT_NOTIFICATION_MODULE_OUTPUT_MS) <
millis()) { millis()) {
getExternal(0) ? setExternalOff(0) : setExternalOn(0); setExternalState(0, !getExternal(0));
} }
if (externalTurnedOn[1] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms if (externalTurnedOn[1] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms
: EXT_NOTIFICATION_MODULE_OUTPUT_MS) < : EXT_NOTIFICATION_MODULE_OUTPUT_MS) <
millis()) { millis()) {
getExternal(1) ? setExternalOff(1) : setExternalOn(1); setExternalState(0, !getExternal(1));
} }
if (externalTurnedOn[2] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms if (externalTurnedOn[2] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms
: EXT_NOTIFICATION_MODULE_OUTPUT_MS) < : EXT_NOTIFICATION_MODULE_OUTPUT_MS) <
millis()) { millis()) {
getExternal(2) ? setExternalOff(2) : setExternalOn(2); setExternalState(0, !getExternal(2));
} }
#if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE) #if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE)
red = (colorState & 4) ? brightnessValues[brightnessIndex] : 0; // Red enabled on colorState = 4,5,6,7 red = (colorState & 4) ? brightnessValues[brightnessIndex] : 0; // Red enabled on colorState = 4,5,6,7
@ -203,86 +203,42 @@ bool ExternalNotificationModule::wantPacket(const meshtastic_MeshPacket *p)
} }
/** /**
* Sets the external notification on for the specified index. * Sets the external notification for the specified index.
* *
* @param index The index of the external notification to turn on. * @param index The index of the external notification to change state.
* @param on Whether we are turning things on (true) or off (false).
*/ */
void ExternalNotificationModule::setExternalOn(uint8_t index) void ExternalNotificationModule::setExternalState(uint8_t index, bool on)
{ {
externalCurrentState[index] = 1; externalCurrentState[index] = on;
externalTurnedOn[index] = millis(); externalTurnedOn[index] = millis();
switch (index) { switch (index) {
case 1: case 1:
#ifdef UNPHONE #ifdef UNPHONE
unphone.vibe(true); // the unPhone's vibration motor is on a i2c GPIO expander unphone.vibe(on); // the unPhone's vibration motor is on a i2c GPIO expander
#endif #endif
if (moduleConfig.external_notification.output_vibra) if (moduleConfig.external_notification.output_vibra)
digitalWrite(moduleConfig.external_notification.output_vibra, true); digitalWrite(moduleConfig.external_notification.output_vibra, on);
break; break;
case 2: case 2:
if (moduleConfig.external_notification.output_buzzer) if (moduleConfig.external_notification.output_buzzer)
digitalWrite(moduleConfig.external_notification.output_buzzer, true); digitalWrite(moduleConfig.external_notification.output_buzzer, on);
break; break;
default: default:
if (output > 0) if (output > 0)
digitalWrite(output, (moduleConfig.external_notification.active ? true : false)); digitalWrite(output, (moduleConfig.external_notification.active ? on : !on));
break;
}
#ifdef HAS_NCP5623
if (rgb_found.type == ScanI2C::NCP5623) {
rgb.setColor(red, green, blue);
}
#endif
#ifdef RGBLED_CA
analogWrite(RGBLED_RED, 255 - red); // CA type needs reverse logic
analogWrite(RGBLED_GREEN, 255 - green);
analogWrite(RGBLED_BLUE, 255 - blue);
#elif defined(RGBLED_RED)
analogWrite(RGBLED_RED, red);
analogWrite(RGBLED_GREEN, green);
analogWrite(RGBLED_BLUE, blue);
#endif
#ifdef HAS_NEOPIXEL
pixels.fill(pixels.Color(red, green, blue), 0, NEOPIXEL_COUNT);
pixels.show();
#endif
#ifdef UNPHONE
unphone.rgb(red, green, blue);
#endif
#ifdef T_WATCH_S3
drv.go();
#endif
}
void ExternalNotificationModule::setExternalOff(uint8_t index)
{
externalCurrentState[index] = 0;
externalTurnedOn[index] = millis();
switch (index) {
case 1:
#ifdef UNPHONE
unphone.vibe(false); // the unPhone's vibration motor is on a i2c GPIO expander
#endif
if (moduleConfig.external_notification.output_vibra)
digitalWrite(moduleConfig.external_notification.output_vibra, false);
break;
case 2:
if (moduleConfig.external_notification.output_buzzer)
digitalWrite(moduleConfig.external_notification.output_buzzer, false);
break;
default:
if (output > 0)
digitalWrite(output, (moduleConfig.external_notification.active ? false : true));
break; break;
} }
#if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE) #if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE)
red = 0; if (!on) {
green = 0; red = 0;
blue = 0; green = 0;
blue = 0;
}
#endif
#ifdef HAS_NCP5623 #ifdef HAS_NCP5623
if (rgb_found.type == ScanI2C::NCP5623) { if (rgb_found.type == ScanI2C::NCP5623) {
rgb.setColor(red, green, blue); rgb.setColor(red, green, blue);
@ -304,10 +260,12 @@ void ExternalNotificationModule::setExternalOff(uint8_t index)
#ifdef UNPHONE #ifdef UNPHONE
unphone.rgb(red, green, blue); unphone.rgb(red, green, blue);
#endif #endif
#endif
#ifdef T_WATCH_S3 #ifdef T_WATCH_S3
drv.stop(); if (on) {
drv.go();
} else {
drv.stop();
}
#endif #endif
} }
@ -379,19 +337,19 @@ ExternalNotificationModule::ExternalNotificationModule()
LOG_INFO("Using Pin %i in digital mode", output); LOG_INFO("Using Pin %i in digital mode", output);
pinMode(output, OUTPUT); pinMode(output, OUTPUT);
} }
setExternalOff(0); setExternalState(0, false);
externalTurnedOn[0] = 0; externalTurnedOn[0] = 0;
if (moduleConfig.external_notification.output_vibra) { if (moduleConfig.external_notification.output_vibra) {
LOG_INFO("Using Pin %i for vibra motor", moduleConfig.external_notification.output_vibra); LOG_INFO("Using Pin %i for vibra motor", moduleConfig.external_notification.output_vibra);
pinMode(moduleConfig.external_notification.output_vibra, OUTPUT); pinMode(moduleConfig.external_notification.output_vibra, OUTPUT);
setExternalOff(1); setExternalState(1, false);
externalTurnedOn[1] = 0; externalTurnedOn[1] = 0;
} }
if (moduleConfig.external_notification.output_buzzer) { if (moduleConfig.external_notification.output_buzzer) {
if (!moduleConfig.external_notification.use_pwm) { if (!moduleConfig.external_notification.use_pwm) {
LOG_INFO("Using Pin %i for buzzer", moduleConfig.external_notification.output_buzzer); LOG_INFO("Using Pin %i for buzzer", moduleConfig.external_notification.output_buzzer);
pinMode(moduleConfig.external_notification.output_buzzer, OUTPUT); pinMode(moduleConfig.external_notification.output_buzzer, OUTPUT);
setExternalOff(2); setExternalState(2, false);
externalTurnedOn[2] = 0; externalTurnedOn[2] = 0;
} else { } else {
config.device.buzzer_gpio = config.device.buzzer_gpio ? config.device.buzzer_gpio : PIN_BUZZER; config.device.buzzer_gpio = config.device.buzzer_gpio ? config.device.buzzer_gpio : PIN_BUZZER;
@ -449,7 +407,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
if (containsBell) { if (containsBell) {
LOG_INFO("externalNotificationModule - Notification Bell"); LOG_INFO("externalNotificationModule - Notification Bell");
isNagging = true; isNagging = true;
setExternalOn(0); setExternalState(0, true);
if (moduleConfig.external_notification.nag_timeout) { if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else { } else {
@ -462,7 +420,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
if (containsBell) { if (containsBell) {
LOG_INFO("externalNotificationModule - Notification Bell (Vibra)"); LOG_INFO("externalNotificationModule - Notification Bell (Vibra)");
isNagging = true; isNagging = true;
setExternalOn(1); setExternalState(1, true);
if (moduleConfig.external_notification.nag_timeout) { if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else { } else {
@ -476,7 +434,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
LOG_INFO("externalNotificationModule - Notification Bell (Buzzer)"); LOG_INFO("externalNotificationModule - Notification Bell (Buzzer)");
isNagging = true; isNagging = true;
if (!moduleConfig.external_notification.use_pwm) { if (!moduleConfig.external_notification.use_pwm) {
setExternalOn(2); setExternalState(2, true);
} else { } else {
#ifdef HAS_I2S #ifdef HAS_I2S
audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone)); audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone));
@ -495,7 +453,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
if (moduleConfig.external_notification.alert_message) { if (moduleConfig.external_notification.alert_message) {
LOG_INFO("externalNotificationModule - Notification Module"); LOG_INFO("externalNotificationModule - Notification Module");
isNagging = true; isNagging = true;
setExternalOn(0); setExternalState(0, true);
if (moduleConfig.external_notification.nag_timeout) { if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else { } else {
@ -506,7 +464,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
if (moduleConfig.external_notification.alert_message_vibra) { if (moduleConfig.external_notification.alert_message_vibra) {
LOG_INFO("externalNotificationModule - Notification Module (Vibra)"); LOG_INFO("externalNotificationModule - Notification Module (Vibra)");
isNagging = true; isNagging = true;
setExternalOn(1); setExternalState(1, true);
if (moduleConfig.external_notification.nag_timeout) { if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000; nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else { } else {
@ -518,7 +476,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
LOG_INFO("externalNotificationModule - Notification Module (Buzzer)"); LOG_INFO("externalNotificationModule - Notification Module (Buzzer)");
isNagging = true; isNagging = true;
if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) { if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) {
setExternalOn(2); setExternalState(2, true);
} else { } else {
#ifdef HAS_I2S #ifdef HAS_I2S
if (moduleConfig.external_notification.use_i2s_as_buzzer) { if (moduleConfig.external_notification.use_i2s_as_buzzer) {

View File

@ -34,8 +34,7 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
uint32_t nagCycleCutoff = 1; uint32_t nagCycleCutoff = 1;
void setExternalOn(uint8_t index = 0); void setExternalState(uint8_t index = 0, bool on = false);
void setExternalOff(uint8_t index = 0);
bool getExternal(uint8_t index = 0); bool getExternal(uint8_t index = 0);
void setMute(bool mute) { isMuted = mute; } void setMute(bool mute) { isMuted = mute; }

View File

@ -121,9 +121,7 @@ Will be used for broadcast.
*/ */
int32_t NeighborInfoModule::runOnce() int32_t NeighborInfoModule::runOnce()
{ {
if (airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) { sendNeighborInfo(NODENUM_BROADCAST_NO_LORA, false);
sendNeighborInfo(NODENUM_BROADCAST_NO_LORA, false);
}
return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs); return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs);
} }

View File

@ -81,6 +81,12 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
ignoreRequest = false; // Don't ignore requests anymore ignoreRequest = false; // Don't ignore requests anymore
meshtastic_User &u = owner; meshtastic_User &u = owner;
// Strip the public key if the user is licensed
if (u.is_licensed && u.public_key.size > 0) {
u.public_key.bytes[0] = 0;
u.public_key.size = 0;
}
LOG_INFO("sending owner %s/%s/%s", u.id, u.long_name, u.short_name); LOG_INFO("sending owner %s/%s/%s", u.id, u.long_name, u.short_name);
lastSentToMesh = millis(); lastSentToMesh = millis();
return allocDataProtobuf(u); return allocDataProtobuf(u);

View File

@ -315,7 +315,7 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp
// LOG_DEBUG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s", // LOG_DEBUG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s",
// nodeDB->getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes); // nodeDB->getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
if (!isFromUs(&mp)) { if (isFromUs(&mp)) {
/* /*
* If moduleConfig.serial.echo is true, then echo the packets that are sent out * If moduleConfig.serial.echo is true, then echo the packets that are sent out

View File

@ -51,7 +51,11 @@ void updateBatteryLevel(uint8_t level) {}
void getMacAddr(uint8_t *dmac) void getMacAddr(uint8_t *dmac)
{ {
#if defined(CONFIG_IDF_TARGET_ESP32C6) && defined(CONFIG_SOC_IEEE802154_SUPPORTED)
assert(esp_base_mac_addr_get(dmac) == ESP_OK);
#else
assert(esp_efuse_mac_get_default(dmac) == ESP_OK); assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
#endif
} }
#ifdef HAS_32768HZ #ifdef HAS_32768HZ

View File

@ -11,6 +11,7 @@
#include "PortduinoGlue.h" #include "PortduinoGlue.h"
#include "linux/gpio/LinuxGPIOPin.h" #include "linux/gpio/LinuxGPIOPin.h"
#include "yaml-cpp/yaml.h" #include "yaml-cpp/yaml.h"
#include <filesystem>
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <unistd.h> #include <unistd.h>
@ -100,33 +101,22 @@ void portduinoSetup()
settingsStrings[displayspidev] = ""; settingsStrings[displayspidev] = "";
settingsMap[spiSpeed] = 2000000; settingsMap[spiSpeed] = 2000000;
settingsMap[ascii_logs] = !isatty(1); settingsMap[ascii_logs] = !isatty(1);
settingsMap[displayPanel] = no_screen;
settingsMap[touchscreenModule] = no_touchscreen;
YAML::Node yamlConfig; YAML::Node yamlConfig;
if (configPath != nullptr) { if (configPath != nullptr) {
std::cout << "Using " << configPath << " as config file" << std::endl; if (loadConfig(configPath)) {
try { std::cout << "Using " << configPath << " as config file" << std::endl;
yamlConfig = YAML::LoadFile(configPath); } else {
} catch (YAML::Exception &e) { std::cout << "Unable to use " << configPath << " as config file" << std::endl;
std::cout << "Could not open " << configPath << " because of error: " << e.what() << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} else if (access("config.yaml", R_OK) == 0) { } else if (access("config.yaml", R_OK) == 0 && loadConfig("config.yaml")) {
std::cout << "Using local config.yaml as config file" << std::endl; std::cout << "Using local config.yaml as config file" << std::endl;
try { } else if (access("/etc/meshtasticd/config.yaml", R_OK) == 0 && loadConfig("/etc/meshtasticd/config.yaml")) {
yamlConfig = YAML::LoadFile("config.yaml");
} catch (YAML::Exception &e) {
std::cout << "*** Exception " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
} else if (access("/etc/meshtasticd/config.yaml", R_OK) == 0) {
std::cout << "Using /etc/meshtasticd/config.yaml as config file" << std::endl; std::cout << "Using /etc/meshtasticd/config.yaml as config file" << std::endl;
try {
yamlConfig = YAML::LoadFile("/etc/meshtasticd/config.yaml");
} catch (YAML::Exception &e) {
std::cout << "*** Exception " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
} else { } else {
std::cout << "No 'config.yaml' found, running simulated." << std::endl; std::cout << "No 'config.yaml' found, running simulated." << std::endl;
settingsMap[maxnodes] = 200; // Default to 200 nodes settingsMap[maxnodes] = 200; // Default to 200 nodes
@ -136,170 +126,21 @@ void portduinoSetup()
return; return;
} }
if (settingsStrings[config_directory] != "") {
std::string filetype = ".yaml";
for (const std::filesystem::directory_entry &entry :
std::filesystem::directory_iterator{settingsStrings[config_directory]}) {
if (ends_with(entry.path().string(), ".yaml")) {
std::cout << "Also using " << entry << " as additional config file" << std::endl;
loadConfig(entry.path().c_str());
}
}
}
// Rather important to set this, if not running simulated. // Rather important to set this, if not running simulated.
randomSeed(time(NULL)); randomSeed(time(NULL));
try { gpioChipName += std::to_string(settingsMap[gpiochip]);
if (yamlConfig["Logging"]) {
if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "trace") {
settingsMap[logoutputlevel] = level_trace;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "debug") {
settingsMap[logoutputlevel] = level_debug;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "info") {
settingsMap[logoutputlevel] = level_info;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "warn") {
settingsMap[logoutputlevel] = level_warn;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "error") {
settingsMap[logoutputlevel] = level_error;
}
settingsStrings[traceFilename] = yamlConfig["Logging"]["TraceFile"].as<std::string>("");
if (yamlConfig["Logging"]["AsciiLogs"]) {
// Default is !isatty(1) but can be set explicitly in config.yaml
settingsMap[ascii_logs] = yamlConfig["Logging"]["AsciiLogs"].as<bool>();
}
}
if (yamlConfig["Lora"]) {
settingsMap[use_sx1262] = false;
settingsMap[use_rf95] = false;
settingsMap[use_sx1280] = false;
settingsMap[use_sx1268] = false;
if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as<std::string>("") == "sx1262") {
settingsMap[use_sx1262] = true;
} else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as<std::string>("") == "RF95") {
settingsMap[use_rf95] = true;
} else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as<std::string>("") == "sx1280") {
settingsMap[use_sx1280] = true;
} else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as<std::string>("") == "sx1268") {
settingsMap[use_sx1268] = true;
}
settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as<bool>(false);
settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as<bool>(false);
settingsMap[cs] = yamlConfig["Lora"]["CS"].as<int>(RADIOLIB_NC);
settingsMap[irq] = yamlConfig["Lora"]["IRQ"].as<int>(RADIOLIB_NC);
settingsMap[busy] = yamlConfig["Lora"]["Busy"].as<int>(RADIOLIB_NC);
settingsMap[reset] = yamlConfig["Lora"]["Reset"].as<int>(RADIOLIB_NC);
settingsMap[txen] = yamlConfig["Lora"]["TXen"].as<int>(RADIOLIB_NC);
settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as<int>(RADIOLIB_NC);
settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as<int>(RADIOLIB_NC);
settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as<int>(0);
settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as<bool>(false);
settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as<int>(2000000);
gpioChipName += std::to_string(settingsMap[gpiochip]);
settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as<std::string>("spidev0.0");
if (settingsStrings[spidev].length() == 14) {
int x = settingsStrings[spidev].at(11) - '0';
int y = settingsStrings[spidev].at(13) - '0';
if (x >= 0 && x < 10 && y >= 0 && y < 10) {
settingsMap[spidev] = x + y << 4;
settingsMap[displayspidev] = settingsMap[spidev];
settingsMap[touchscreenspidev] = settingsMap[spidev];
}
}
}
if (yamlConfig["GPIO"]) {
settingsMap[user] = yamlConfig["GPIO"]["User"].as<int>(RADIOLIB_NC);
}
if (yamlConfig["GPS"]) {
std::string serialPath = yamlConfig["GPS"]["SerialPath"].as<std::string>("");
if (serialPath != "") {
Serial1.setPath(serialPath);
settingsMap[has_gps] = 1;
}
}
if (yamlConfig["I2C"]) {
settingsStrings[i2cdev] = yamlConfig["I2C"]["I2CDevice"].as<std::string>("");
}
settingsMap[displayPanel] = no_screen;
if (yamlConfig["Display"]) {
if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7789")
settingsMap[displayPanel] = st7789;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7735")
settingsMap[displayPanel] = st7735;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7735S")
settingsMap[displayPanel] = st7735s;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7796")
settingsMap[displayPanel] = st7796;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ILI9341")
settingsMap[displayPanel] = ili9341;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ILI9342")
settingsMap[displayPanel] = ili9342;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ILI9488")
settingsMap[displayPanel] = ili9488;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "HX8357D")
settingsMap[displayPanel] = hx8357d;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "X11")
settingsMap[displayPanel] = x11;
settingsMap[displayHeight] = yamlConfig["Display"]["Height"].as<int>(0);
settingsMap[displayWidth] = yamlConfig["Display"]["Width"].as<int>(0);
settingsMap[displayDC] = yamlConfig["Display"]["DC"].as<int>(-1);
settingsMap[displayCS] = yamlConfig["Display"]["CS"].as<int>(-1);
settingsMap[displayRGBOrder] = yamlConfig["Display"]["RGBOrder"].as<bool>(false);
settingsMap[displayBacklight] = yamlConfig["Display"]["Backlight"].as<int>(-1);
settingsMap[displayBacklightInvert] = yamlConfig["Display"]["BacklightInvert"].as<bool>(false);
settingsMap[displayBacklightPWMChannel] = yamlConfig["Display"]["BacklightPWMChannel"].as<int>(-1);
settingsMap[displayReset] = yamlConfig["Display"]["Reset"].as<int>(-1);
settingsMap[displayOffsetX] = yamlConfig["Display"]["OffsetX"].as<int>(0);
settingsMap[displayOffsetY] = yamlConfig["Display"]["OffsetY"].as<int>(0);
settingsMap[displayRotate] = yamlConfig["Display"]["Rotate"].as<bool>(false);
settingsMap[displayOffsetRotate] = yamlConfig["Display"]["OffsetRotate"].as<int>(1);
settingsMap[displayInvert] = yamlConfig["Display"]["Invert"].as<bool>(false);
settingsMap[displayBusFrequency] = yamlConfig["Display"]["BusFrequency"].as<int>(40000000);
if (yamlConfig["Display"]["spidev"]) {
settingsStrings[displayspidev] = "/dev/" + yamlConfig["Display"]["spidev"].as<std::string>("spidev0.1");
if (settingsStrings[displayspidev].length() == 14) {
int x = settingsStrings[displayspidev].at(11) - '0';
int y = settingsStrings[displayspidev].at(13) - '0';
if (x >= 0 && x < 10 && y >= 0 && y < 10) {
settingsMap[displayspidev] = x + y << 4;
settingsMap[touchscreenspidev] = settingsMap[displayspidev];
}
}
}
}
settingsMap[touchscreenModule] = no_touchscreen;
if (yamlConfig["Touchscreen"]) {
if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "XPT2046")
settingsMap[touchscreenModule] = xpt2046;
else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "STMPE610")
settingsMap[touchscreenModule] = stmpe610;
else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "GT911")
settingsMap[touchscreenModule] = gt911;
else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "FT5x06")
settingsMap[touchscreenModule] = ft5x06;
settingsMap[touchscreenCS] = yamlConfig["Touchscreen"]["CS"].as<int>(-1);
settingsMap[touchscreenIRQ] = yamlConfig["Touchscreen"]["IRQ"].as<int>(-1);
settingsMap[touchscreenBusFrequency] = yamlConfig["Touchscreen"]["BusFrequency"].as<int>(1000000);
settingsMap[touchscreenRotate] = yamlConfig["Touchscreen"]["Rotate"].as<int>(-1);
settingsMap[touchscreenI2CAddr] = yamlConfig["Touchscreen"]["I2CAddr"].as<int>(-1);
if (yamlConfig["Touchscreen"]["spidev"]) {
settingsStrings[touchscreenspidev] = "/dev/" + yamlConfig["Touchscreen"]["spidev"].as<std::string>("");
if (settingsStrings[touchscreenspidev].length() == 14) {
int x = settingsStrings[touchscreenspidev].at(11) - '0';
int y = settingsStrings[touchscreenspidev].at(13) - '0';
if (x >= 0 && x < 10 && y >= 0 && y < 10) {
settingsMap[touchscreenspidev] = x + y << 4;
}
}
}
}
if (yamlConfig["Input"]) {
settingsStrings[keyboardDevice] = (yamlConfig["Input"]["KeyboardDevice"]).as<std::string>("");
}
if (yamlConfig["Webserver"]) {
settingsMap[webserverport] = (yamlConfig["Webserver"]["Port"]).as<int>(-1);
settingsStrings[webserverrootpath] = (yamlConfig["Webserver"]["RootPath"]).as<std::string>("");
}
settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as<int>(200);
settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as<int>(100);
} catch (YAML::Exception &e) {
std::cout << "*** Exception " << e.what() << std::endl;
exit(EXIT_FAILURE);
}
for (configNames i : GPIO_lines) { for (configNames i : GPIO_lines) {
if (settingsMap.count(i) && settingsMap[i] > max_GPIO) if (settingsMap.count(i) && settingsMap[i] > max_GPIO)
@ -402,4 +243,178 @@ int initGPIOPin(int pinNum, const std::string gpioChipName)
#else #else
return ERRNO_OK; return ERRNO_OK;
#endif #endif
}
bool loadConfig(const char *configPath)
{
YAML::Node yamlConfig;
try {
yamlConfig = YAML::LoadFile(configPath);
if (yamlConfig["Logging"]) {
if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "trace") {
settingsMap[logoutputlevel] = level_trace;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "debug") {
settingsMap[logoutputlevel] = level_debug;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "info") {
settingsMap[logoutputlevel] = level_info;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "warn") {
settingsMap[logoutputlevel] = level_warn;
} else if (yamlConfig["Logging"]["LogLevel"].as<std::string>("info") == "error") {
settingsMap[logoutputlevel] = level_error;
}
settingsStrings[traceFilename] = yamlConfig["Logging"]["TraceFile"].as<std::string>("");
if (yamlConfig["Logging"]["AsciiLogs"]) {
// Default is !isatty(1) but can be set explicitly in config.yaml
settingsMap[ascii_logs] = yamlConfig["Logging"]["AsciiLogs"].as<bool>();
}
}
if (yamlConfig["Lora"]) {
settingsMap[use_sx1262] = false;
settingsMap[use_rf95] = false;
settingsMap[use_sx1280] = false;
settingsMap[use_sx1268] = false;
if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as<std::string>("") == "sx1262") {
settingsMap[use_sx1262] = true;
} else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as<std::string>("") == "RF95") {
settingsMap[use_rf95] = true;
} else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as<std::string>("") == "sx1280") {
settingsMap[use_sx1280] = true;
} else if (yamlConfig["Lora"]["Module"] && yamlConfig["Lora"]["Module"].as<std::string>("") == "sx1268") {
settingsMap[use_sx1268] = true;
}
settingsMap[dio2_as_rf_switch] = yamlConfig["Lora"]["DIO2_AS_RF_SWITCH"].as<bool>(false);
settingsMap[dio3_tcxo_voltage] = yamlConfig["Lora"]["DIO3_TCXO_VOLTAGE"].as<bool>(false);
settingsMap[cs] = yamlConfig["Lora"]["CS"].as<int>(RADIOLIB_NC);
settingsMap[irq] = yamlConfig["Lora"]["IRQ"].as<int>(RADIOLIB_NC);
settingsMap[busy] = yamlConfig["Lora"]["Busy"].as<int>(RADIOLIB_NC);
settingsMap[reset] = yamlConfig["Lora"]["Reset"].as<int>(RADIOLIB_NC);
settingsMap[txen] = yamlConfig["Lora"]["TXen"].as<int>(RADIOLIB_NC);
settingsMap[rxen] = yamlConfig["Lora"]["RXen"].as<int>(RADIOLIB_NC);
settingsMap[sx126x_ant_sw] = yamlConfig["Lora"]["SX126X_ANT_SW"].as<int>(RADIOLIB_NC);
settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as<int>(0);
settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as<bool>(false);
settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as<int>(2000000);
settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as<std::string>("spidev0.0");
if (settingsStrings[spidev].length() == 14) {
int x = settingsStrings[spidev].at(11) - '0';
int y = settingsStrings[spidev].at(13) - '0';
if (x >= 0 && x < 10 && y >= 0 && y < 10) {
settingsMap[spidev] = x + y << 4;
settingsMap[displayspidev] = settingsMap[spidev];
settingsMap[touchscreenspidev] = settingsMap[spidev];
}
}
}
if (yamlConfig["GPIO"]) {
settingsMap[user] = yamlConfig["GPIO"]["User"].as<int>(RADIOLIB_NC);
}
if (yamlConfig["GPS"]) {
std::string serialPath = yamlConfig["GPS"]["SerialPath"].as<std::string>("");
if (serialPath != "") {
Serial1.setPath(serialPath);
settingsMap[has_gps] = 1;
}
}
if (yamlConfig["I2C"]) {
settingsStrings[i2cdev] = yamlConfig["I2C"]["I2CDevice"].as<std::string>("");
}
if (yamlConfig["Display"]) {
if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7789")
settingsMap[displayPanel] = st7789;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7735")
settingsMap[displayPanel] = st7735;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7735S")
settingsMap[displayPanel] = st7735s;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ST7796")
settingsMap[displayPanel] = st7796;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ILI9341")
settingsMap[displayPanel] = ili9341;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ILI9342")
settingsMap[displayPanel] = ili9342;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "ILI9488")
settingsMap[displayPanel] = ili9488;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "HX8357D")
settingsMap[displayPanel] = hx8357d;
else if (yamlConfig["Display"]["Panel"].as<std::string>("") == "X11")
settingsMap[displayPanel] = x11;
settingsMap[displayHeight] = yamlConfig["Display"]["Height"].as<int>(0);
settingsMap[displayWidth] = yamlConfig["Display"]["Width"].as<int>(0);
settingsMap[displayDC] = yamlConfig["Display"]["DC"].as<int>(-1);
settingsMap[displayCS] = yamlConfig["Display"]["CS"].as<int>(-1);
settingsMap[displayRGBOrder] = yamlConfig["Display"]["RGBOrder"].as<bool>(false);
settingsMap[displayBacklight] = yamlConfig["Display"]["Backlight"].as<int>(-1);
settingsMap[displayBacklightInvert] = yamlConfig["Display"]["BacklightInvert"].as<bool>(false);
settingsMap[displayBacklightPWMChannel] = yamlConfig["Display"]["BacklightPWMChannel"].as<int>(-1);
settingsMap[displayReset] = yamlConfig["Display"]["Reset"].as<int>(-1);
settingsMap[displayOffsetX] = yamlConfig["Display"]["OffsetX"].as<int>(0);
settingsMap[displayOffsetY] = yamlConfig["Display"]["OffsetY"].as<int>(0);
settingsMap[displayRotate] = yamlConfig["Display"]["Rotate"].as<bool>(false);
settingsMap[displayOffsetRotate] = yamlConfig["Display"]["OffsetRotate"].as<int>(1);
settingsMap[displayInvert] = yamlConfig["Display"]["Invert"].as<bool>(false);
settingsMap[displayBusFrequency] = yamlConfig["Display"]["BusFrequency"].as<int>(40000000);
if (yamlConfig["Display"]["spidev"]) {
settingsStrings[displayspidev] = "/dev/" + yamlConfig["Display"]["spidev"].as<std::string>("spidev0.1");
if (settingsStrings[displayspidev].length() == 14) {
int x = settingsStrings[displayspidev].at(11) - '0';
int y = settingsStrings[displayspidev].at(13) - '0';
if (x >= 0 && x < 10 && y >= 0 && y < 10) {
settingsMap[displayspidev] = x + y << 4;
settingsMap[touchscreenspidev] = settingsMap[displayspidev];
}
}
}
}
if (yamlConfig["Touchscreen"]) {
if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "XPT2046")
settingsMap[touchscreenModule] = xpt2046;
else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "STMPE610")
settingsMap[touchscreenModule] = stmpe610;
else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "GT911")
settingsMap[touchscreenModule] = gt911;
else if (yamlConfig["Touchscreen"]["Module"].as<std::string>("") == "FT5x06")
settingsMap[touchscreenModule] = ft5x06;
settingsMap[touchscreenCS] = yamlConfig["Touchscreen"]["CS"].as<int>(-1);
settingsMap[touchscreenIRQ] = yamlConfig["Touchscreen"]["IRQ"].as<int>(-1);
settingsMap[touchscreenBusFrequency] = yamlConfig["Touchscreen"]["BusFrequency"].as<int>(1000000);
settingsMap[touchscreenRotate] = yamlConfig["Touchscreen"]["Rotate"].as<int>(-1);
settingsMap[touchscreenI2CAddr] = yamlConfig["Touchscreen"]["I2CAddr"].as<int>(-1);
if (yamlConfig["Touchscreen"]["spidev"]) {
settingsStrings[touchscreenspidev] = "/dev/" + yamlConfig["Touchscreen"]["spidev"].as<std::string>("");
if (settingsStrings[touchscreenspidev].length() == 14) {
int x = settingsStrings[touchscreenspidev].at(11) - '0';
int y = settingsStrings[touchscreenspidev].at(13) - '0';
if (x >= 0 && x < 10 && y >= 0 && y < 10) {
settingsMap[touchscreenspidev] = x + y << 4;
}
}
}
}
if (yamlConfig["Input"]) {
settingsStrings[keyboardDevice] = (yamlConfig["Input"]["KeyboardDevice"]).as<std::string>("");
}
if (yamlConfig["Webserver"]) {
settingsMap[webserverport] = (yamlConfig["Webserver"]["Port"]).as<int>(-1);
settingsStrings[webserverrootpath] = (yamlConfig["Webserver"]["RootPath"]).as<std::string>("");
}
if (yamlConfig["General"]) {
settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as<int>(200);
settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as<int>(100);
settingsStrings[config_directory] = (yamlConfig["General"]["ConfigDirectory"]).as<std::string>("");
}
} catch (YAML::Exception &e) {
std::cout << "*** Exception " << e.what() << std::endl;
return false;
}
return true;
}
// https://stackoverflow.com/questions/874134/find-out-if-string-ends-with-another-string-in-c
static bool ends_with(std::string_view str, std::string_view suffix)
{
return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
} }

View File

@ -55,7 +55,8 @@ enum configNames {
webserverrootpath, webserverrootpath,
maxtophone, maxtophone,
maxnodes, maxnodes,
ascii_logs ascii_logs,
config_directory
}; };
enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9488, hx8357d }; enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9488, hx8357d };
enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 }; enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 };
@ -64,4 +65,6 @@ enum { level_error, level_warn, level_info, level_debug, level_trace };
extern std::map<configNames, int> settingsMap; extern std::map<configNames, int> settingsMap;
extern std::map<configNames, std::string> settingsStrings; extern std::map<configNames, std::string> settingsStrings;
extern std::ofstream traceFile; extern std::ofstream traceFile;
int initGPIOPin(int pinNum, std::string gpioChipname); int initGPIOPin(int pinNum, std::string gpioChipname);
bool loadConfig(const char *configPath);
static bool ends_with(std::string_view str, std::string_view suffix);

View File

@ -19,4 +19,3 @@ static const uint8_t MOSI = 38;
static const uint8_t SS = 17; static const uint8_t SS = 17;
#endif /* Pins_Arduino_h */ #endif /* Pins_Arduino_h */

View File

@ -1,4 +1,4 @@
[VERSION] [VERSION]
major = 2 major = 2
minor = 5 minor = 5
build = 9 build = 11