diff --git a/.github/workflows/main_matrix.yml b/.github/workflows/main_matrix.yml
index 0853df19f8..efdbd26373 100644
--- a/.github/workflows/main_matrix.yml
+++ b/.github/workflows/main_matrix.yml
@@ -245,7 +245,8 @@ jobs:
if: ${{ github.event_name == 'workflow_dispatch' }}
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
- needs: [
+ needs:
+ [
gather-artifacts,
package-raspbian,
package-raspbian-armv7l,
diff --git a/.github/workflows/package_amd64.yml b/.github/workflows/package_amd64.yml
index 0b5093f248..a5442246aa 100644
--- a/.github/workflows/package_amd64.yml
+++ b/.github/workflows/package_amd64.yml
@@ -50,11 +50,14 @@ jobs:
mkdir -p .debpkg/usr/share/doc/meshtasticd/web
mkdir -p .debpkg/usr/sbin
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/
tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web
gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz
cp release/meshtasticd_linux_x86_64 .debpkg/usr/sbin/meshtasticd
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
cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service
echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles
diff --git a/.github/workflows/package_raspbian.yml b/.github/workflows/package_raspbian.yml
index bcbda53e27..89efba1ded 100644
--- a/.github/workflows/package_raspbian.yml
+++ b/.github/workflows/package_raspbian.yml
@@ -50,11 +50,14 @@ jobs:
mkdir -p .debpkg/usr/share/doc/meshtasticd/web
mkdir -p .debpkg/usr/sbin
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/
tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web
gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz
cp release/meshtasticd_linux_aarch64 .debpkg/usr/sbin/meshtasticd
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
cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service
echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles
diff --git a/.github/workflows/package_raspbian_armv7l.yml b/.github/workflows/package_raspbian_armv7l.yml
index 1308fe925f..5cbc270974 100644
--- a/.github/workflows/package_raspbian_armv7l.yml
+++ b/.github/workflows/package_raspbian_armv7l.yml
@@ -50,11 +50,14 @@ jobs:
mkdir -p .debpkg/usr/share/doc/meshtasticd/web
mkdir -p .debpkg/usr/sbin
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/
tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web
gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz
cp release/meshtasticd_linux_armv7l .debpkg/usr/sbin/meshtasticd
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
cp bin/meshtasticd.service .debpkg/usr/lib/systemd/system/meshtasticd.service
echo "/etc/meshtasticd/config.yaml" > .debpkg/DEBIAN/conffiles
diff --git a/.github/workflows/stale_bot.yml b/.github/workflows/stale_bot.yml
new file mode 100644
index 0000000000..0fd2cd5c39
--- /dev/null
+++ b/.github/workflows/stale_bot.yml
@@ -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
diff --git a/.github/workflows/trunk_format_pr.yml b/.github/workflows/trunk_format_pr.yml
new file mode 100644
index 0000000000..c5c20b4657
--- /dev/null
+++ b/.github/workflows/trunk_format_pr.yml
@@ -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.'
+ })
diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml
index ea4045a165..7e77baa0bd 100644
--- a/.trunk/trunk.yaml
+++ b/.trunk/trunk.yaml
@@ -1,22 +1,22 @@
version: 0.1
cli:
- version: 1.22.6
+ version: 1.22.7
plugins:
sources:
- id: trunk
- ref: v1.6.3
+ ref: v1.6.4
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- - trufflehog@3.82.6
+ - trufflehog@3.83.2
- yamllint@1.35.1
- bandit@1.7.10
- - checkov@3.2.256
- - terrascan@1.19.1
- - trivy@0.55.2
+ - checkov@3.2.276
+ - terrascan@1.19.9
+ - trivy@0.56.2
#- trufflehog@3.63.2-rc0
- taplo@0.9.3
- - ruff@0.6.8
+ - ruff@0.7.1
- isort@5.13.2
- markdownlint@0.42.0
- oxipng@9.1.2
@@ -26,9 +26,9 @@ lint:
- hadolint@2.12.0
- shfmt@3.6.0
- shellcheck@0.10.0
- - black@24.8.0
+ - black@24.10.0
- git-diff-check
- - gitleaks@8.20.0
+ - gitleaks@8.21.1
- clang-format@16.0.3
- prettier@3.3.3
ignore:
diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini
index 36d8b9542b..382975e9f4 100644
--- a/arch/esp32/esp32.ini
+++ b/arch/esp32/esp32.ini
@@ -31,7 +31,7 @@ build_flags =
-DCONFIG_BT_NIMBLE_ENABLED
-DCONFIG_NIMBLE_CPP_LOG_LEVEL=2
-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
-DSERIAL_BUFFER_SIZE=4096
-DLIBPAX_ARDUINO
diff --git a/arch/nrf52/nrf52.ini b/arch/nrf52/nrf52.ini
index 04880d540e..1af68198bf 100644
--- a/arch/nrf52/nrf52.ini
+++ b/arch/nrf52/nrf52.ini
@@ -23,4 +23,5 @@ lib_deps=
rweather/Crypto@^0.4.0
lib_ignore =
- BluetoothOTA
\ No newline at end of file
+ BluetoothOTA
+ lvgl
diff --git a/arch/portduino/portduino.ini b/arch/portduino/portduino.ini
index 8778c32a0d..7eb563d771 100644
--- a/arch/portduino/portduino.ini
+++ b/arch/portduino/portduino.ini
@@ -24,7 +24,7 @@ lib_deps =
${env.lib_deps}
${networking_base.lib_deps}
rweather/Crypto@^0.4.0
- lovyan03/LovyanGFX@^1.1.16
+ https://github.com/lovyan03/LovyanGFX.git#1401c28a47646fe00538d487adcb2eb3c72de805
build_flags =
${arduino_base.build_flags}
diff --git a/arch/rp2xx0/rp2350.ini b/arch/rp2xx0/rp2350.ini
index 96ed0cb217..7ef6332e32 100644
--- a/arch/rp2xx0/rp2350.ini
+++ b/arch/rp2xx0/rp2350.ini
@@ -16,8 +16,9 @@ build_src_filter =
lib_ignore =
BluetoothOTA
+ lvgl
lib_deps =
${arduino_base.lib_deps}
${environmental_base.lib_deps}
- rweather/Crypto
\ No newline at end of file
+ rweather/Crypto
diff --git a/bin/config-dist.yaml b/bin/config-dist.yaml
index a755954b1f..bf1331a2b0 100644
--- a/bin/config-dist.yaml
+++ b/bin/config-dist.yaml
@@ -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
### Uncomment the block that corresponds to your hardware
### Including the "Module:" line!
---
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!
# CS: 7
@@ -115,6 +111,29 @@ Display:
# Height: 320
# Rotate: true
+### SHCHV 3.5 RPi TFT+Touchscreen
+# Panel: ILI9486
+# spidev: spidev0.0
+# BusFrequency: 30000000
+# DC: 24
+# Reset: 25
+# Width: 320
+# Height: 480
+# OffsetRotate: 2
+
+### TZT 2.0 Inch TFT Display ST7789V 240RGBx320
+# Panel: ST7789
+# spidev: spidev0.0
+# # CS: 8 # can be freely chosen
+# BusFrequency: 80000000
+# DC: 24 # can be freely chosen
+# Width: 320
+# Height: 240
+# Reset: 25 # can be freely chosen
+# Rotate: true
+# OffsetRotate: 1
+# Invert: true
+
### You can also specify the spi device for the display to use
# spidev: spidev0.0
@@ -156,3 +175,4 @@ Webserver:
General:
MaxNodes: 200
MaxMessageQueue: 100
+ ConfigDirectory: /etc/meshtasticd/config.d/
\ No newline at end of file
diff --git a/bin/config.d/display-waveshare-2.8.yaml b/bin/config.d/display-waveshare-2.8.yaml
new file mode 100644
index 0000000000..2e28276d8a
--- /dev/null
+++ b/bin/config.d/display-waveshare-2.8.yaml
@@ -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
\ No newline at end of file
diff --git a/bin/config.d/lora-waveshare-sxxx.yaml b/bin/config.d/lora-waveshare-sxxx.yaml
new file mode 100644
index 0000000000..a9ff136530
--- /dev/null
+++ b/bin/config.d/lora-waveshare-sxxx.yaml
@@ -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
diff --git a/boards/bpi_picow_esp32_s3.json b/boards/bpi_picow_esp32_s3.json
index 9a20dd57f9..75983d8450 100644
--- a/boards/bpi_picow_esp32_s3.json
+++ b/boards/bpi_picow_esp32_s3.json
@@ -28,6 +28,8 @@
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
+ "use_1200bps_touch": true,
+ "wait_for_upload_port": true,
"require_upload_port": true,
"speed": 921600
},
diff --git a/boards/icarus.json b/boards/icarus.json
new file mode 100644
index 0000000000..03da4682fc
--- /dev/null
+++ b/boards/icarus.json
@@ -0,0 +1,41 @@
+{
+ "build": {
+ "arduino": {
+ "ldscript": "esp32s3_out.ld",
+ "memory_type": "qio_opi"
+ },
+ "core": "esp32",
+ "extra_flags": [
+ "-DBOARD_HAS_PSRAM",
+ "-DARDUINO_USB_CDC_ON_BOOT=1",
+ "-DARDUINO_USB_MODE=0",
+ "-DARDUINO_RUNNING_CORE=1",
+ "-DARDUINO_EVENT_RUNNING_CORE=0"
+ ],
+ "f_cpu": "240000000L",
+ "f_flash": "80000000L",
+ "flash_mode": "qio",
+ "hwids": [["0x2886", "0x0059"]],
+ "mcu": "esp32s3",
+ "variant": "icarus"
+ },
+ "connectivity": ["wifi", "bluetooth", "lora"],
+ "debug": {
+ "default_tool": "esp-builtin",
+ "onboard_tools": ["esp-builtin"],
+ "openocd_target": "esp32s3.cfg"
+ },
+ "frameworks": ["arduino", "espidf"],
+ "name": "icarus",
+ "upload": {
+ "flash_size": "8MB",
+ "maximum_ram_size": 8388608,
+ "maximum_size": 8388608,
+ "use_1200bps_touch": true,
+ "wait_for_upload_port": true,
+ "require_upload_port": true,
+ "speed": 921600
+ },
+ "url": "https://icarus.azlan.works",
+ "vendor": "Muhammad Shah"
+}
diff --git a/boards/unphone.json b/boards/unphone.json
new file mode 100644
index 0000000000..bf711993c1
--- /dev/null
+++ b/boards/unphone.json
@@ -0,0 +1,46 @@
+{
+ "build": {
+ "arduino": {
+ "ldscript": "esp32s3_out.ld",
+ "memory_type": "qio_opi",
+ "partitions": "default_8MB.csv"
+ },
+ "core": "esp32",
+ "extra_flags": [
+ "-DBOARD_HAS_PSRAM",
+ "-DUNPHONE_SPIN=9",
+ "-DARDUINO_USB_CDC_ON_BOOT=1",
+ "-DARDUINO_USB_MODE=0",
+ "-DARDUINO_RUNNING_CORE=1",
+ "-DARDUINO_EVENT_RUNNING_CORE=1"
+ ],
+ "f_cpu": "240000000L",
+ "f_flash": "80000000L",
+ "flash_mode": "qio",
+ "hwids": [
+ ["0x16D0", "0x1178"],
+ ["0x303a", "0x1001"]
+ ],
+ "mcu": "esp32s3",
+ "variant": "unphone"
+ },
+ "connectivity": ["wifi", "bluetooth", "lora"],
+ "debug": {
+ "default_tool": "esp-builtin",
+ "onboard_tools": ["esp-builtin"],
+ "openocd_target": "esp32s3.cfg"
+ },
+ "frameworks": ["arduino", "espidf"],
+ "name": "unPhone",
+ "upload": {
+ "flash_size": "8MB",
+ "maximum_ram_size": 327680,
+ "maximum_size": 8323072,
+ "use_1200bps_touch": true,
+ "wait_for_upload_port": true,
+ "require_upload_port": true,
+ "speed": 921600
+ },
+ "url": "https://unphone.net/",
+ "vendor": "University of Sheffield"
+}
diff --git a/protobufs b/protobufs
index 8686d049c2..807236815d 160000
--- a/protobufs
+++ b/protobufs
@@ -1 +1 @@
-Subproject commit 8686d049c22c232f57121e66dfb29e7be65010f0
+Subproject commit 807236815d61cc0ebd89472c2a2aa76758bc92ac
diff --git a/src/Power.cpp b/src/Power.cpp
index f9b04dfd55..f8d2459bdf 100644
--- a/src/Power.cpp
+++ b/src/Power.cpp
@@ -154,9 +154,16 @@ static void adcEnable()
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
#ifdef ADC_USE_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
pinMode(ADC_CTRL, OUTPUT);
digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED);
+#endif
#endif
delay(10);
#endif
@@ -167,10 +174,14 @@ static void adcDisable()
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP
pinMode(ADC_CTRL, INPUT_PULLDOWN);
+#else
+#ifdef HELTEC_V3
+ pinMode(ADC_CTRL, ANALOG);
#else
digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED);
#endif
#endif
+#endif
}
#endif
@@ -360,7 +371,12 @@ class AnalogBatteryLevel : public HasBatteryLevel
/**
* return true if there is a battery installed in this unit
*/
+ // if we have a integrated device with a battery, we can assume that the battery is always connected
+#ifdef BATTERY_IMMUTABLE
+ virtual bool isBatteryConnect() override { return true; }
+#else
virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; }
+#endif
/// If we see a battery voltage higher than physics allows - assume charger is pumping
/// in power
diff --git a/src/RedirectablePrint.h b/src/RedirectablePrint.h
index 4ae2b68f94..45b62b7af2 100644
--- a/src/RedirectablePrint.h
+++ b/src/RedirectablePrint.h
@@ -51,9 +51,9 @@ class RedirectablePrint : public Print
protected:
/// Subclasses can override if they need to change how we format over the serial port
virtual void log_to_serial(const char *logLevel, const char *format, va_list arg);
+ meshtastic_LogRecord_Level getLogLevel(const char *logLevel);
private:
void log_to_syslog(const char *logLevel, const char *format, va_list arg);
void log_to_ble(const char *logLevel, const char *format, va_list arg);
- meshtastic_LogRecord_Level getLogLevel(const char *logLevel);
};
\ No newline at end of file
diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp
index fe6ccdefe1..68c41980d0 100644
--- a/src/SerialConsole.cpp
+++ b/src/SerialConsole.cpp
@@ -99,25 +99,7 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg)
{
if (usingProtobufs && config.security.debug_log_api_enabled) {
- meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset
- switch (logLevel[0]) {
- case 'D':
- ll = meshtastic_LogRecord_Level_DEBUG;
- break;
- case 'I':
- ll = meshtastic_LogRecord_Level_INFO;
- break;
- case 'W':
- ll = meshtastic_LogRecord_Level_WARNING;
- break;
- case 'E':
- ll = meshtastic_LogRecord_Level_ERROR;
- break;
- case 'C':
- ll = meshtastic_LogRecord_Level_CRITICAL;
- break;
- }
-
+ meshtastic_LogRecord_Level ll = RedirectablePrint::getLogLevel(logLevel);
auto thread = concurrency::OSThread::currentThread;
emitLogRecord(ll, thread ? thread->ThreadName.c_str() : "", format, arg);
} else
diff --git a/src/configuration.h b/src/configuration.h
index 66070eb403..cb2326470c 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -136,6 +136,7 @@ along with this program. If not, see .
#define LPS22HB_ADDR_ALT 0x5D
#define SHT31_4x_ADDR 0x44
#define PMSA0031_ADDR 0x12
+#define QMA6100P_ADDR 0x12
#define AHT10_ADDR 0x38
#define RCWL9620_ADDR 0x57
#define VEML7700_ADDR 0x10
diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp
index 41ba8257e2..4caa0f730b 100644
--- a/src/detect/ScanI2C.cpp
+++ b/src/detect/ScanI2C.cpp
@@ -37,8 +37,8 @@ ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
{
- ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948};
- return firstOfOrNONE(7, types);
+ ScanI2C::DeviceType types[] = {MPU6050, LIS3DH, BMA423, LSM6DS3, BMX160, STK8BAXX, ICM20948, QMA6100P};
+ return firstOfOrNONE(8, types);
}
ScanI2C::FoundDevice ScanI2C::find(ScanI2C::DeviceType) const
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index b2c482c980..8591b8433c 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -39,6 +39,7 @@ class ScanI2C
QMC5883L,
HMC5883L,
PMSA0031,
+ QMA6100P,
MPU6050,
LIS3DH,
BMA423,
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 71e475904f..d39c9899c5 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -7,7 +7,8 @@
#include "linux/LinuxHardwareI2C.h"
#endif
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
-#include "main.h" // atecc
+#include "main.h" // atecc
+#include "meshUtils.h" // vformat
#endif
// AXP192 and AXP2101 have the same device address, we just need to identify it in Power.cpp
@@ -172,7 +173,16 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
}
#endif
- for (addr.address = 1; addr.address < 127; addr.address++) {
+ // We only need to scan 112 addresses, the rest is reserved for special purposes
+ // 0x00 General Call
+ // 0x01 CBUS addresses
+ // 0x02 Reserved for different bus formats
+ // 0x03 Reserved for future purposes
+ // 0x04-0x07 High Speed Master Code
+ // 0x78-0x7B 10-bit slave addressing
+ // 0x7C-0x7F Reserved for future purposes
+
+ for (addr.address = 8; addr.address < 120; addr.address++) {
if (asize != 0) {
if (!in_array(address, asize, addr.address))
continue;
@@ -394,7 +404,11 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(QMC5883L_ADDR, QMC5883L, "QMC5883L Highrate 3-Axis magnetic sensor found")
SCAN_SIMPLE_CASE(HMC5883L_ADDR, HMC5883L, "HMC5883L 3-Axis digital compass found")
+#ifdef HAS_QMA6100P
+ SCAN_SIMPLE_CASE(QMA6100P_ADDR, QMA6100P, "QMA6100P accelerometer found")
+#else
SCAN_SIMPLE_CASE(PMSA0031_ADDR, PMSA0031, "PMSA0031 air quality sensor found")
+#endif
SCAN_SIMPLE_CASE(BMA423_ADDR, BMA423, "BMA423 accelerometer found");
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3 accelerometer found at address 0x%x", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TCA9535_ADDR, TCA9535, "TCA9535 I2C expander found");
diff --git a/src/detect/axpDebug.h b/src/detect/axpDebug.h
deleted file mode 100644
index fd75745f25..0000000000
--- a/src/detect/axpDebug.h
+++ /dev/null
@@ -1,19 +0,0 @@
-#if 0
-// Turn off for now
-uint32_t axpDebugRead()
-{
- axp.debugCharging();
- LOG_DEBUG("vbus current %f", axp.getVbusCurrent());
- LOG_DEBUG("charge current %f", axp.getBattChargeCurrent());
- LOG_DEBUG("bat voltage %f", axp.getBattVoltage());
- LOG_DEBUG("batt pct %d", axp.getBattPercentage());
- LOG_DEBUG("is battery connected %d", axp.isBatteryConnect());
- LOG_DEBUG("is USB connected %d", axp.isVBUSPlug());
- LOG_DEBUG("is charging %d", axp.isChargeing());
-
- return 30 * 1000;
-}
-
-Periodic axpDebugOutput(axpDebugRead);
-axpDebugOutput.setup();
-#endif
\ No newline at end of file
diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp
index be18546c44..2302e200de 100644
--- a/src/gps/GPS.cpp
+++ b/src/gps/GPS.cpp
@@ -20,6 +20,7 @@
#ifdef ARCH_PORTDUINO
#include "PortduinoGlue.h"
#include "meshUtils.h"
+#include
#include
#endif
@@ -443,7 +444,7 @@ bool GPS::setup()
gnssModel = probe(rareSerialSpeeds[speedSelect]);
if (gnssModel == GNSS_MODEL_UNKNOWN) {
if (++speedSelect == sizeof(rareSerialSpeeds) / sizeof(int)) {
- LOG_WARN("Giving up on GPS probe and setting to 9600.");
+ LOG_WARN("Giving up on GPS probe and setting to %d", GPS_BAUDRATE);
return true;
}
return false;
@@ -488,6 +489,18 @@ bool GPS::setup()
// Switch to Fitness Mode, for running and walking purpose with low speed (<5 m/s)
_serial_gps->write("$PMTK886,1*29\r\n");
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) {
// 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);
@@ -1157,6 +1170,7 @@ GnssModel_t GPS::probe(int serialSpeed)
delay(20);
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};
UBXChecksum(cfg_rate, sizeof(cfg_rate));
diff --git a/src/gps/GPS.h b/src/gps/GPS.h
index 55fc649f3a..808c4008b7 100644
--- a/src/gps/GPS.h
+++ b/src/gps/GPS.h
@@ -34,6 +34,7 @@ typedef enum {
GNSS_MODEL_UC6580,
GNSS_MODEL_UNKNOWN,
GNSS_MODEL_MTK_L76B,
+ GNSS_MODEL_MTK_PA1616S,
GNSS_MODEL_AG3335,
GNSS_MODEL_AG3352
} GnssModel_t;
@@ -75,8 +76,10 @@ class GPS : private concurrency::OSThread
uint8_t fixType = 0; // fix type from GPGSA
#endif
private:
+
const int serialSpeeds[6] = {9600, 115200, 38400};
- const int rareSerialSpeeds[6] = {4800, 57600, 9600};
+ const int rareSerialSpeeds[6] = {4800, 57600, GPS_BAUDRATE};
+
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
uint32_t rx_gpio = 0;
uint32_t tx_gpio = 0;
diff --git a/src/gps/GPSUpdateScheduling.cpp b/src/gps/GPSUpdateScheduling.cpp
index 9c2626e848..abcf6b196f 100644
--- a/src/gps/GPSUpdateScheduling.cpp
+++ b/src/gps/GPSUpdateScheduling.cpp
@@ -70,9 +70,9 @@ bool GPSUpdateScheduling::isUpdateDue()
// Have we been searching for a GPS position for too long?
bool GPSUpdateScheduling::searchedTooLong()
{
- uint32_t maxSearchMs =
- Default::getConfiguredOrDefaultMs(config.position.position_broadcast_secs, default_broadcast_interval_secs);
-
+ 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;
@@ -115,4 +115,4 @@ void GPSUpdateScheduling::updateLockTimePrediction()
uint32_t GPSUpdateScheduling::predictedSearchDurationMs()
{
return GPSUpdateScheduling::predictedMsToGetLock;
-}
\ No newline at end of file
+}
diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp
index 7ad2eb2aca..c1f35ba3c3 100644
--- a/src/input/cardKbI2cImpl.cpp
+++ b/src/input/cardKbI2cImpl.cpp
@@ -9,7 +9,7 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {}
void CardKbI2cImpl::init()
{
-#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO)
+#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(I2C_NO_RESCAN)
if (cardkb_found.address == 0x00) {
LOG_DEBUG("Rescanning for I2C keyboard");
uint8_t i2caddr_scan[] = {CARDKB_ADDR, TDECK_KB_ADDR, BBQ10_KB_ADDR, MPR121_KB_ADDR};
@@ -49,7 +49,7 @@ void CardKbI2cImpl::init()
kb_model = 0x00;
}
}
- LOG_DEBUG("Keyboard Type: 0x%02x Model: 0x%02x Address: 0x%02x\n", kb_info.type, kb_model, cardkb_found.address);
+ LOG_DEBUG("Keyboard Type: 0x%02x Model: 0x%02x Address: 0x%02x", kb_info.type, kb_model, cardkb_found.address);
if (cardkb_found.address == 0x00) {
disable();
return;
diff --git a/src/main.cpp b/src/main.cpp
index 3c1406a65e..0ce6b3bae6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -27,7 +27,6 @@
#include "detect/ScanI2CTwoWire.h"
#include
#endif
-#include "detect/axpDebug.h"
#include "detect/einkScan.h"
#include "graphics/RAKled.h"
#include "graphics/Screen.h"
@@ -267,14 +266,19 @@ void setup()
#ifdef DEBUG_PORT
consoleInit(); // Set serial baud rate and init our mesh console
#endif
+
+#ifdef UNPHONE
+ unphone.printStore();
+#endif
+
#if ARCH_PORTDUINO
struct timeval tv;
tv.tv_sec = time(NULL);
tv.tv_usec = 0;
perhapsSetRTC(RTCQualityNTP, &tv);
#endif
- powerMonInit();
+ powerMonInit();
serialSinceMsec = millis();
LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n");
@@ -643,6 +647,8 @@ void setup()
rp2040Setup();
#endif
+ initSPI(); // needed here before reading from littleFS
+
// We do this as early as possible because this loads preferences from flash
// but we need to do this after main cpu init (esp32setup), because we need the random seed set
nodeDB = new NodeDB;
@@ -706,7 +712,6 @@ void setup()
#endif
// Init our SPI controller (must be before screen and lora)
- initSPI();
#ifdef ARCH_RP2040
#ifdef HW_SPI1_DEVICE
SPI1.setSCK(LORA_SCK);
diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 78ccdd85ad..247cbd2545 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -32,9 +32,14 @@
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
+#include "SPILock.h"
#include "modules/StoreForwardModule.h"
#include
+#include
+#include
#include
+#include
+#include
#endif
#ifdef ARCH_PORTDUINO
@@ -109,6 +114,44 @@ NodeDB::NodeDB()
uint32_t channelFileCRC = crc32Buffer(&channelFile, sizeof(channelFile));
int saveWhat = 0;
+ bool hasUniqueId = false;
+ // Get device unique id
+#if defined(ARCH_ESP32) && defined(ESP_EFUSE_OPTIONAL_UNIQUE_ID)
+ uint32_t unique_id[4];
+ // ESP32 factory burns a unique id in efuse for S2+ series and evidently C3+ series
+ // This is used for HMACs in the esp-rainmaker AIOT platform and seems to be a good choice for us
+ esp_err_t err = esp_efuse_read_field_blob(ESP_EFUSE_OPTIONAL_UNIQUE_ID, unique_id, sizeof(unique_id) * 8);
+ if (err == ESP_OK) {
+ memcpy(myNodeInfo.device_id.bytes, unique_id, sizeof(unique_id));
+ myNodeInfo.device_id.size = 16;
+ hasUniqueId = true;
+ } else {
+ LOG_WARN("Failed to read unique id from efuse");
+ }
+#elif defined(ARCH_NRF52)
+ // Nordic applies a FIPS compliant Random ID to each chip at the factory
+ // We concatenate the device address to the Random ID to create a unique ID for now
+ // This will likely utilize a crypto module in the future
+ uint64_t device_id_start = ((uint64_t)NRF_FICR->DEVICEID[1] << 32) | NRF_FICR->DEVICEID[0];
+ uint64_t device_id_end = ((uint64_t)NRF_FICR->DEVICEADDR[1] << 32) | NRF_FICR->DEVICEADDR[0];
+ 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));
+ myNodeInfo.device_id.size = 16;
+ // Uncomment below to print the device id
+ // hasUniqueId = true;
+#else
+ // FIXME - implement for other platforms
+#endif
+
+ // if (hasUniqueId) {
+ // std::string deviceIdHex;
+ // for (size_t i = 0; i < myNodeInfo.device_id.size; ++i) {
+ // char buf[3];
+ // snprintf(buf, sizeof(buf), "%02X", myNodeInfo.device_id.bytes[i]);
+ // deviceIdHex += buf;
+ // }
+ // LOG_DEBUG("Device ID (HEX): %s", deviceIdHex.c_str());
+ // }
// likewise - we always want the app requirements to come from the running appload
myNodeInfo.min_app_version = 30200; // format is Mmmss (where M is 1+the numeric major number. i.e. 30200 means 2.2.00
@@ -132,21 +175,23 @@ NodeDB::NodeDB()
}
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN || MESHTASTIC_EXCLUDE_PKI)
- bool keygenSuccess = false;
- if (config.security.private_key.size == 32) {
- if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
+ if (!owner.is_licensed) {
+ bool keygenSuccess = false;
+ 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;
}
- } else {
- LOG_INFO("Generating new PKI keys");
- crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
- keygenSuccess = true;
- }
- 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);
+ 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)
// Calculate Curve25519 public and private keys
@@ -515,8 +560,10 @@ void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
if (role == meshtastic_Config_DeviceConfig_Role_ROUTER) {
initConfigIntervals();
initModuleConfigIntervals();
+ config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY;
} else if (role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
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) {
moduleConfig.telemetry.environment_measurement_enabled = true;
moduleConfig.telemetry.environment_update_interval = 300;
@@ -829,6 +876,9 @@ void NodeDB::loadFromDisk()
bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct,
bool fullAtomic)
{
+#ifdef ARCH_ESP32
+ concurrency::LockGuard g(spiLock);
+#endif
bool okay = false;
#ifdef FSCom
auto f = SafeFile(filename, fullAtomic);
@@ -1229,4 +1279,4 @@ void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, co
LOG_ERROR("A critical failure occurred, portduino is exiting...");
exit(2);
#endif
-}
+}
\ No newline at end of file
diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp
index f51c9bcb95..e8f6d1c070 100644
--- a/src/mesh/RadioInterface.cpp
+++ b/src/mesh/RadioInterface.cpp
@@ -271,7 +271,7 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
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 {
// offset the maximum delay for routers: (2 * CWmax * slotTimeMsec)
delay = (2 * CWmax * slotTimeMsec) + random(0, pow(2, CWsize)) * slotTimeMsec;
diff --git a/src/mesh/RadioLibInterface.cpp b/src/mesh/RadioLibInterface.cpp
index f44d50d363..3b20cda144 100644
--- a/src/mesh/RadioLibInterface.cpp
+++ b/src/mesh/RadioLibInterface.cpp
@@ -20,9 +20,9 @@ void LockingArduinoHal::spiBeginTransaction()
void LockingArduinoHal::spiEndTransaction()
{
- spiLock->unlock();
-
ArduinoHal::spiEndTransaction();
+
+ spiLock->unlock();
}
#if ARCH_PORTDUINO
void LockingArduinoHal::spiTransfer(uint8_t *out, size_t len, uint8_t *in)
@@ -278,11 +278,14 @@ void RadioLibInterface::onNotify(uint32_t notification)
// Send any outgoing packets we have ready
meshtastic_MeshPacket *txp = txQueue.dequeue();
assert(txp);
+ bool isLoraTx = txp->to != NODENUM_BROADCAST_NO_LORA;
startSend(txp);
- // Packet has been sent, count it toward our TX airtime utilization.
- uint32_t xmitMsec = getPacketTime(txp);
- airTime->logAirtime(TX_LOG, xmitMsec);
+ if (isLoraTx) {
+ // Packet has been sent, count it toward our TX airtime utilization.
+ uint32_t xmitMsec = getPacketTime(txp);
+ airTime->logAirtime(TX_LOG, xmitMsec);
+ }
}
}
} else {
diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp
index 84ae7bbb02..e1eb8eddf0 100644
--- a/src/mesh/Router.cpp
+++ b/src/mesh/Router.cpp
@@ -81,14 +81,17 @@ int32_t Router::runOnce()
*/
void Router::enqueueReceivedMessage(meshtastic_MeshPacket *p)
{
- if (fromRadioQueue.enqueue(p, 0)) { // NOWAIT - fixme, if queue is full, delete older messages
-
- // Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME
- setReceivedMessage();
- } else {
- printPacket("BUG! fromRadioQueue is full! Discarding!", p);
- packetPool.release(p);
+ // Try enqueue until successful
+ while (!fromRadioQueue.enqueue(p, 0)) {
+ meshtastic_MeshPacket *old_p;
+ old_p = fromRadioQueue.dequeuePtr(0); // Dequeue and discard the oldest packet
+ if (old_p) {
+ printPacket("fromRadioQ full, drop oldest!", old_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
@@ -591,19 +594,20 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
skipHandle = true;
}
+ bool shouldIgnoreNonstandardPorts =
+ config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY;
#if USERPREFS_EVENT_MODE
- if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
- (p->decoded.portnum == meshtastic_PortNum_ATAK_FORWARDER || p->decoded.portnum == meshtastic_PortNum_ATAK_PLUGIN ||
- p->decoded.portnum == meshtastic_PortNum_PAXCOUNTER_APP || p->decoded.portnum == meshtastic_PortNum_IP_TUNNEL_APP ||
- p->decoded.portnum == meshtastic_PortNum_AUDIO_APP || p->decoded.portnum == meshtastic_PortNum_PRIVATE_APP ||
- p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP ||
- p->decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP ||
- p->decoded.portnum == meshtastic_PortNum_REMOTE_HARDWARE_APP)) {
- LOG_DEBUG("Ignoring packet on blacklisted portnum during event");
+ shouldIgnoreNonstandardPorts = true;
+#endif
+ if (shouldIgnoreNonstandardPorts && p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
+ IS_ONE_OF(p->decoded.portnum, meshtastic_PortNum_ATAK_FORWARDER, meshtastic_PortNum_ATAK_PLUGIN,
+ meshtastic_PortNum_PAXCOUNTER_APP, meshtastic_PortNum_IP_TUNNEL_APP, meshtastic_PortNum_AUDIO_APP,
+ meshtastic_PortNum_PRIVATE_APP, meshtastic_PortNum_DETECTION_SENSOR_APP, meshtastic_PortNum_RANGE_TEST_APP,
+ meshtastic_PortNum_REMOTE_HARDWARE_APP)) {
+ LOG_DEBUG("Ignoring packet on blacklisted portnum for CORE_PORTNUMS_ONLY");
cancelSending(p->from, p->id);
skipHandle = true;
}
-#endif
} else {
printPacket("packet decoding failed or skipped (no PSK?)", p);
}
diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h
index 4c76e74cbf..fab23ae34f 100644
--- a/src/mesh/generated/meshtastic/config.pb.h
+++ b/src/mesh/generated/meshtastic/config.pb.h
@@ -4,6 +4,7 @@
#ifndef PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_CONFIG_PB_H_INCLUDED
#include
+#include "meshtastic/device_ui.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
@@ -576,6 +577,7 @@ typedef struct _meshtastic_Config {
meshtastic_Config_BluetoothConfig bluetooth;
meshtastic_Config_SecurityConfig security;
meshtastic_Config_SessionkeyConfig sessionkey;
+ meshtastic_DeviceUIConfig device_ui;
} payload_variant;
} meshtastic_Config;
@@ -779,6 +781,7 @@ extern "C" {
#define meshtastic_Config_bluetooth_tag 7
#define meshtastic_Config_security_tag 8
#define meshtastic_Config_sessionkey_tag 9
+#define meshtastic_Config_device_ui_tag 10
/* Struct field encoding specification for nanopb */
#define meshtastic_Config_FIELDLIST(X, a) \
@@ -790,7 +793,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,display,payload_variant.disp
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,lora,payload_variant.lora), 6) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,bluetooth,payload_variant.bluetooth), 7) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,security,payload_variant.security), 8) \
-X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sessionkey,payload_variant.sessionkey), 9)
+X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sessionkey,payload_variant.sessionkey), 9) \
+X(a, STATIC, ONEOF, MESSAGE, (payload_variant,device_ui,payload_variant.device_ui), 10)
#define meshtastic_Config_CALLBACK NULL
#define meshtastic_Config_DEFAULT NULL
#define meshtastic_Config_payload_variant_device_MSGTYPE meshtastic_Config_DeviceConfig
@@ -802,6 +806,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,sessionkey,payload_variant.s
#define meshtastic_Config_payload_variant_bluetooth_MSGTYPE meshtastic_Config_BluetoothConfig
#define meshtastic_Config_payload_variant_security_MSGTYPE meshtastic_Config_SecurityConfig
#define meshtastic_Config_payload_variant_sessionkey_MSGTYPE meshtastic_Config_SessionkeyConfig
+#define meshtastic_Config_payload_variant_device_ui_MSGTYPE meshtastic_DeviceUIConfig
#define meshtastic_Config_DeviceConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, role, 1) \
diff --git a/src/mesh/generated/meshtastic/device_ui.pb.h b/src/mesh/generated/meshtastic/device_ui.pb.h
index 469fe6f11f..5c1e067ab4 100644
--- a/src/mesh/generated/meshtastic/device_ui.pb.h
+++ b/src/mesh/generated/meshtastic/device_ui.pb.h
@@ -40,7 +40,19 @@ typedef enum _meshtastic_Language {
/* Polish */
meshtastic_Language_POLISH = 8,
/* 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;
/* Struct definitions */
@@ -73,16 +85,22 @@ typedef struct _meshtastic_NodeHighlight {
} meshtastic_NodeHighlight;
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 */
uint8_t screen_brightness;
/* Screen timeout 0..900 */
uint16_t screen_timeout;
- /* Screen lock enabled */
+ /* Screen/Settings lock enabled */
bool screen_lock;
+ bool settings_lock;
+ uint32_t pin_code;
/* Color theme */
meshtastic_Theme theme;
- /* Audible message alert enabled */
+ /* Audible message, banner and ring tone */
bool alert_enabled;
+ bool banner_enabled;
+ uint8_t ring_tone_id;
/* Localization */
meshtastic_Language language;
/* Node list filter */
@@ -104,8 +122,8 @@ extern "C" {
#define _meshtastic_Theme_ARRAYSIZE ((meshtastic_Theme)(meshtastic_Theme_RED+1))
#define _meshtastic_Language_MIN meshtastic_Language_ENGLISH
-#define _meshtastic_Language_MAX meshtastic_Language_TURKISH
-#define _meshtastic_Language_ARRAYSIZE ((meshtastic_Language)(meshtastic_Language_TURKISH+1))
+#define _meshtastic_Language_MAX meshtastic_Language_TRADITIONAL_CHINESE
+#define _meshtastic_Language_ARRAYSIZE ((meshtastic_Language)(meshtastic_Language_TRADITIONAL_CHINESE+1))
#define meshtastic_DeviceUIConfig_theme_ENUMTYPE meshtastic_Theme
#define meshtastic_DeviceUIConfig_language_ENUMTYPE meshtastic_Language
@@ -114,10 +132,10 @@ extern "C" {
/* 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_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_NodeHighlight_init_zero {0, 0, 0, 0, ""}
@@ -133,25 +151,35 @@ extern "C" {
#define meshtastic_NodeHighlight_telemetry_switch_tag 3
#define meshtastic_NodeHighlight_iaq_switch_tag 4
#define meshtastic_NodeHighlight_node_name_tag 5
-#define meshtastic_DeviceUIConfig_screen_brightness_tag 1
-#define meshtastic_DeviceUIConfig_screen_timeout_tag 2
-#define meshtastic_DeviceUIConfig_screen_lock_tag 3
-#define meshtastic_DeviceUIConfig_theme_tag 4
-#define meshtastic_DeviceUIConfig_alert_enabled_tag 5
-#define meshtastic_DeviceUIConfig_language_tag 6
-#define meshtastic_DeviceUIConfig_node_filter_tag 7
-#define meshtastic_DeviceUIConfig_node_highlight_tag 8
+#define meshtastic_DeviceUIConfig_version_tag 1
+#define meshtastic_DeviceUIConfig_screen_brightness_tag 2
+#define meshtastic_DeviceUIConfig_screen_timeout_tag 3
+#define meshtastic_DeviceUIConfig_screen_lock_tag 4
+#define meshtastic_DeviceUIConfig_settings_lock_tag 5
+#define meshtastic_DeviceUIConfig_pin_code_tag 6
+#define meshtastic_DeviceUIConfig_theme_tag 7
+#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 */
#define meshtastic_DeviceUIConfig_FIELDLIST(X, a) \
-X(a, STATIC, SINGULAR, UINT32, screen_brightness, 1) \
-X(a, STATIC, SINGULAR, UINT32, screen_timeout, 2) \
-X(a, STATIC, SINGULAR, BOOL, screen_lock, 3) \
-X(a, STATIC, SINGULAR, UENUM, theme, 4) \
-X(a, STATIC, SINGULAR, BOOL, alert_enabled, 5) \
-X(a, STATIC, SINGULAR, UENUM, language, 6) \
-X(a, STATIC, OPTIONAL, MESSAGE, node_filter, 7) \
-X(a, STATIC, OPTIONAL, MESSAGE, node_highlight, 8)
+X(a, STATIC, SINGULAR, UINT32, version, 1) \
+X(a, STATIC, SINGULAR, UINT32, screen_brightness, 2) \
+X(a, STATIC, SINGULAR, UINT32, screen_timeout, 3) \
+X(a, STATIC, SINGULAR, BOOL, screen_lock, 4) \
+X(a, STATIC, SINGULAR, BOOL, settings_lock, 5) \
+X(a, STATIC, SINGULAR, UINT32, pin_code, 6) \
+X(a, STATIC, SINGULAR, UENUM, theme, 7) \
+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_DEFAULT NULL
#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) */
#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_NodeHighlight_size 25
diff --git a/src/mesh/generated/meshtastic/rtttl.pb.h b/src/mesh/generated/meshtastic/rtttl.pb.h
index 2b7e35f116..0572265f7a 100644
--- a/src/mesh/generated/meshtastic/rtttl.pb.h
+++ b/src/mesh/generated/meshtastic/rtttl.pb.h
@@ -13,7 +13,7 @@
/* Canned message module configuration. */
typedef struct _meshtastic_RTTTLConfig {
/* Ringtone for PWM Buzzer in RTTTL Format. */
- char ringtone[230];
+ char ringtone[231];
} meshtastic_RTTTLConfig;
@@ -41,7 +41,7 @@ extern const pb_msgdesc_t meshtastic_RTTTLConfig_msg;
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_RTTTL_PB_H_MAX_SIZE meshtastic_RTTTLConfig_size
-#define meshtastic_RTTTLConfig_size 232
+#define meshtastic_RTTTLConfig_size 233
#ifdef __cplusplus
} /* extern "C" */
diff --git a/src/modules/AdminModule.cpp b/src/modules/AdminModule.cpp
index c0580ab335..90722f5472 100644
--- a/src/modules/AdminModule.cpp
+++ b/src/modules/AdminModule.cpp
@@ -900,7 +900,7 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
#ifdef ARCH_PORTDUINO
conn.wifi.status.is_connected = true;
#else
- conn.wifi.status.is_connected = WiFi.status() != WL_CONNECTED;
+ conn.wifi.status.is_connected = WiFi.status() == WL_CONNECTED;
#endif
strncpy(conn.wifi.ssid, config.network.wifi_ssid, 33);
if (conn.wifi.status.is_connected) {
@@ -932,10 +932,14 @@ void AdminModule::handleGetDeviceConnectionStatus(const meshtastic_MeshPacket &r
conn.has_bluetooth = true;
conn.bluetooth.pin = config.bluetooth.fixed_pin;
#ifdef ARCH_ESP32
- conn.bluetooth.is_connected = nimbleBluetooth->isConnected();
- conn.bluetooth.rssi = nimbleBluetooth->getRssi();
+ if (config.bluetooth.enabled && nimbleBluetooth) {
+ conn.bluetooth.is_connected = nimbleBluetooth->isConnected();
+ conn.bluetooth.rssi = nimbleBluetooth->getRssi();
+ }
#elif defined(ARCH_NRF52)
- conn.bluetooth.is_connected = nrf52Bluetooth->isConnected();
+ if (config.bluetooth.enabled && nrf52Bluetooth) {
+ conn.bluetooth.is_connected = nrf52Bluetooth->isConnected();
+ }
#endif
#endif
conn.has_serial = true; // No serial-less devices
diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp
index a35a742068..41ad0ea654 100644
--- a/src/modules/ExternalNotificationModule.cpp
+++ b/src/modules/ExternalNotificationModule.cpp
@@ -93,7 +93,7 @@ int32_t ExternalNotificationModule::runOnce()
nagCycleCutoff = UINT32_MAX;
LOG_INFO("Turning off external notification: ");
for (int i = 0; i < 3; i++) {
- setExternalOff(i);
+ setExternalState(i, false);
externalTurnedOn[i] = 0;
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
: EXT_NOTIFICATION_MODULE_OUTPUT_MS) <
millis()) {
- getExternal(0) ? setExternalOff(0) : setExternalOn(0);
+ setExternalState(0, !getExternal(0));
}
if (externalTurnedOn[1] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms
: EXT_NOTIFICATION_MODULE_OUTPUT_MS) <
millis()) {
- getExternal(1) ? setExternalOff(1) : setExternalOn(1);
+ setExternalState(0, !getExternal(1));
}
if (externalTurnedOn[2] + (moduleConfig.external_notification.output_ms ? moduleConfig.external_notification.output_ms
: EXT_NOTIFICATION_MODULE_OUTPUT_MS) <
millis()) {
- getExternal(2) ? setExternalOff(2) : setExternalOn(2);
+ setExternalState(0, !getExternal(2));
}
#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
@@ -184,7 +184,7 @@ int32_t ExternalNotificationModule::runOnce()
}
#endif
// now let the PWM buzzer play
- if (moduleConfig.external_notification.use_pwm) {
+ if (moduleConfig.external_notification.use_pwm && config.device.buzzer_gpio) {
if (rtttl::isPlaying()) {
rtttl::play();
} else if (isNagging && (nagCycleCutoff >= millis())) {
@@ -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();
switch (index) {
case 1:
#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
if (moduleConfig.external_notification.output_vibra)
- digitalWrite(moduleConfig.external_notification.output_vibra, true);
+ digitalWrite(moduleConfig.external_notification.output_vibra, on);
break;
case 2:
if (moduleConfig.external_notification.output_buzzer)
- digitalWrite(moduleConfig.external_notification.output_buzzer, true);
+ digitalWrite(moduleConfig.external_notification.output_buzzer, on);
break;
default:
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);
+#if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE)
+ if (!on) {
+ red = 0;
+ green = 0;
+ blue = 0;
}
#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;
- }
-
-#if defined(HAS_NCP5623) || defined(RGBLED_RED) || defined(HAS_NEOPIXEL) || defined(UNPHONE)
- red = 0;
- green = 0;
- blue = 0;
#ifdef HAS_NCP5623
if (rgb_found.type == ScanI2C::NCP5623) {
rgb.setColor(red, green, blue);
@@ -304,10 +260,12 @@ void ExternalNotificationModule::setExternalOff(uint8_t index)
#ifdef UNPHONE
unphone.rgb(red, green, blue);
#endif
-#endif
-
#ifdef T_WATCH_S3
- drv.stop();
+ if (on) {
+ drv.go();
+ } else {
+ drv.stop();
+ }
#endif
}
@@ -379,19 +337,19 @@ ExternalNotificationModule::ExternalNotificationModule()
LOG_INFO("Using Pin %i in digital mode", output);
pinMode(output, OUTPUT);
}
- setExternalOff(0);
+ setExternalState(0, false);
externalTurnedOn[0] = 0;
if (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);
- setExternalOff(1);
+ setExternalState(1, false);
externalTurnedOn[1] = 0;
}
if (moduleConfig.external_notification.output_buzzer) {
if (!moduleConfig.external_notification.use_pwm) {
LOG_INFO("Using Pin %i for buzzer", moduleConfig.external_notification.output_buzzer);
pinMode(moduleConfig.external_notification.output_buzzer, OUTPUT);
- setExternalOff(2);
+ setExternalState(2, false);
externalTurnedOn[2] = 0;
} else {
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) {
LOG_INFO("externalNotificationModule - Notification Bell");
isNagging = true;
- setExternalOn(0);
+ setExternalState(0, true);
if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else {
@@ -462,7 +420,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
if (containsBell) {
LOG_INFO("externalNotificationModule - Notification Bell (Vibra)");
isNagging = true;
- setExternalOn(1);
+ setExternalState(1, true);
if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else {
@@ -476,7 +434,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
LOG_INFO("externalNotificationModule - Notification Bell (Buzzer)");
isNagging = true;
if (!moduleConfig.external_notification.use_pwm) {
- setExternalOn(2);
+ setExternalState(2, true);
} else {
#ifdef HAS_I2S
audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone));
@@ -495,7 +453,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
if (moduleConfig.external_notification.alert_message) {
LOG_INFO("externalNotificationModule - Notification Module");
isNagging = true;
- setExternalOn(0);
+ setExternalState(0, true);
if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else {
@@ -506,7 +464,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
if (moduleConfig.external_notification.alert_message_vibra) {
LOG_INFO("externalNotificationModule - Notification Module (Vibra)");
isNagging = true;
- setExternalOn(1);
+ setExternalState(1, true);
if (moduleConfig.external_notification.nag_timeout) {
nagCycleCutoff = millis() + moduleConfig.external_notification.nag_timeout * 1000;
} else {
@@ -518,7 +476,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
LOG_INFO("externalNotificationModule - Notification Module (Buzzer)");
isNagging = true;
if (!moduleConfig.external_notification.use_pwm && !moduleConfig.external_notification.use_i2s_as_buzzer) {
- setExternalOn(2);
+ setExternalState(2, true);
} else {
#ifdef HAS_I2S
if (moduleConfig.external_notification.use_i2s_as_buzzer) {
diff --git a/src/modules/ExternalNotificationModule.h b/src/modules/ExternalNotificationModule.h
index a5dff36518..841ca6de9a 100644
--- a/src/modules/ExternalNotificationModule.h
+++ b/src/modules/ExternalNotificationModule.h
@@ -32,10 +32,9 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
public:
ExternalNotificationModule();
- uint32_t nagCycleCutoff = UINT32_MAX;
+ uint32_t nagCycleCutoff = 1;
- void setExternalOn(uint8_t index = 0);
- void setExternalOff(uint8_t index = 0);
+ void setExternalState(uint8_t index = 0, bool on = false);
bool getExternal(uint8_t index = 0);
void setMute(bool mute) { isMuted = mute; }
diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp
index cbf63e9af9..88d434070d 100644
--- a/src/modules/NeighborInfoModule.cpp
+++ b/src/modules/NeighborInfoModule.cpp
@@ -121,9 +121,7 @@ Will be used for broadcast.
*/
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);
}
diff --git a/src/modules/NodeInfoModule.cpp b/src/modules/NodeInfoModule.cpp
index 843711807e..855cf44918 100644
--- a/src/modules/NodeInfoModule.cpp
+++ b/src/modules/NodeInfoModule.cpp
@@ -81,6 +81,12 @@ meshtastic_MeshPacket *NodeInfoModule::allocReply()
ignoreRequest = false; // Don't ignore requests anymore
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);
lastSentToMesh = millis();
return allocDataProtobuf(u);
diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp
index dc9c8aa85c..1c45a1d404 100644
--- a/src/modules/SerialModule.cpp
+++ b/src/modules/SerialModule.cpp
@@ -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",
// 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
diff --git a/src/modules/Telemetry/AirQualityTelemetry.cpp b/src/modules/Telemetry/AirQualityTelemetry.cpp
index 5947075371..5f7fe5dac5 100644
--- a/src/modules/Telemetry/AirQualityTelemetry.cpp
+++ b/src/modules/Telemetry/AirQualityTelemetry.cpp
@@ -35,6 +35,7 @@ int32_t AirQualityTelemetryModule::runOnce()
if (moduleConfig.telemetry.air_quality_enabled) {
LOG_INFO("Air quality Telemetry: Initializing");
if (!aqi.begin_I2C()) {
+#ifndef I2C_NO_RESCAN
LOG_WARN("Could not establish i2c connection to AQI sensor. Rescanning...");
// rescan for late arriving sensors. AQI Module starts about 10 seconds into the boot so this is plenty.
uint8_t i2caddr_scan[] = {PMSA0031_ADDR};
@@ -51,6 +52,7 @@ int32_t AirQualityTelemetryModule::runOnce()
i2cScanner->fetchI2CBus(found.address);
return 1000;
}
+#endif
return disable();
}
return 1000;
diff --git a/src/motion/AccelerometerThread.h b/src/motion/AccelerometerThread.h
index b9ae926cf4..e548b20c60 100755
--- a/src/motion/AccelerometerThread.h
+++ b/src/motion/AccelerometerThread.h
@@ -14,6 +14,9 @@
#include "LSM6DS3Sensor.h"
#include "MPU6050Sensor.h"
#include "MotionSensor.h"
+#ifdef HAS_QMA6100P
+#include "QMA6100PSensor.h"
+#endif
#include "STK8XXXSensor.h"
extern ScanI2C::DeviceAddress accelerometer_found;
@@ -97,6 +100,11 @@ class AccelerometerThread : public concurrency::OSThread
case ScanI2C::DeviceType::ICM20948:
sensor = new ICM20948Sensor(device);
break;
+#ifdef HAS_QMA6100P
+ case ScanI2C::DeviceType::QMA6100P:
+ sensor = new QMA6100PSensor(device);
+ break;
+#endif
default:
disable();
return;
diff --git a/src/motion/LIS3DHSensor.cpp b/src/motion/LIS3DHSensor.cpp
index 0d608670c4..d06b46b50a 100755
--- a/src/motion/LIS3DHSensor.cpp
+++ b/src/motion/LIS3DHSensor.cpp
@@ -1,4 +1,5 @@
#include "LIS3DHSensor.h"
+#include "NodeDB.h"
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C
diff --git a/src/motion/LSM6DS3Sensor.cpp b/src/motion/LSM6DS3Sensor.cpp
index b3b1a13ec0..3b25c3872b 100755
--- a/src/motion/LSM6DS3Sensor.cpp
+++ b/src/motion/LSM6DS3Sensor.cpp
@@ -1,4 +1,5 @@
#include "LSM6DS3Sensor.h"
+#include "NodeDB.h"
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C
diff --git a/src/motion/MotionSensor.h b/src/motion/MotionSensor.h
index 4da23cc70b..78eec54cec 100755
--- a/src/motion/MotionSensor.h
+++ b/src/motion/MotionSensor.h
@@ -14,6 +14,7 @@
#include "../graphics/Screen.h"
#include "../graphics/ScreenFonts.h"
#include "../power.h"
+#include "Wire.h"
// Base class for motion processing
class MotionSensor
diff --git a/src/motion/QMA6100PSensor.cpp b/src/motion/QMA6100PSensor.cpp
new file mode 100644
index 0000000000..989188fe67
--- /dev/null
+++ b/src/motion/QMA6100PSensor.cpp
@@ -0,0 +1,183 @@
+#include "QMA6100PSensor.h"
+
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_QMA6100P)
+
+// Flag when an interrupt has been detected
+volatile static bool QMA6100P_IRQ = false;
+
+// Interrupt service routine
+void QMA6100PSetInterrupt()
+{
+ QMA6100P_IRQ = true;
+}
+
+QMA6100PSensor::QMA6100PSensor(ScanI2C::FoundDevice foundDevice) : MotionSensor::MotionSensor(foundDevice) {}
+
+bool QMA6100PSensor::init()
+{
+ // Initialise the sensor
+ sensor = QMA6100PSingleton::GetInstance();
+ if (!sensor->init(device))
+ return false;
+
+ // Enable simple Wake on Motion
+ return sensor->setWakeOnMotion();
+}
+
+#ifdef QMA_6100P_INT_PIN
+
+int32_t QMA6100PSensor::runOnce()
+{
+ // Wake on motion using hardware interrupts - this is the most efficient way to check for motion
+ if (QMA6100P_IRQ) {
+ QMA6100P_IRQ = false;
+ wakeScreen();
+ }
+ return MOTION_SENSOR_CHECK_INTERVAL_MS;
+}
+
+#else
+
+int32_t QMA6100PSensor::runOnce()
+{
+ // Wake on motion using polling - this is not as efficient as using hardware interrupt pin (see above)
+
+ uint8_t tempVal;
+ if (!sensor->readRegisterRegion(SFE_QMA6100P_INT_ST0, &tempVal, 1)) {
+ LOG_DEBUG("QMA6100PSensor::isWakeOnMotion failed to read interrupts");
+ return MOTION_SENSOR_CHECK_INTERVAL_MS;
+ }
+
+ if ((tempVal & 7) != 0) {
+ // Wake up!
+ wakeScreen();
+ }
+ return MOTION_SENSOR_CHECK_INTERVAL_MS;
+}
+
+#endif
+
+// ----------------------------------------------------------------------
+// QMA6100PSingleton
+// ----------------------------------------------------------------------
+
+// Get a singleton wrapper for an Sparkfun QMA_6100P_I2C
+QMA6100PSingleton *QMA6100PSingleton::GetInstance()
+{
+ if (pinstance == nullptr) {
+ pinstance = new QMA6100PSingleton();
+ }
+ return pinstance;
+}
+
+QMA6100PSingleton::QMA6100PSingleton() {}
+
+QMA6100PSingleton::~QMA6100PSingleton() {}
+
+QMA6100PSingleton *QMA6100PSingleton::pinstance{nullptr};
+
+// Initialise the QMA6100P Sensor
+bool QMA6100PSingleton::init(ScanI2C::FoundDevice device)
+{
+
+// startup
+#ifdef Wire1
+ bool status = begin(device.address.address, device.address.port == ScanI2C::I2CPort::WIRE1 ? &Wire1 : &Wire);
+#else
+ // check chip id
+ bool status = begin(device.address.address, &Wire);
+#endif
+ if (status != true) {
+ LOG_WARN("QMA6100PSensor::init begin failed\n");
+ return false;
+ }
+ delay(20);
+ // SW reset to make sure the device starts in a known state
+ if (softwareReset() != true) {
+ LOG_WARN("QMA6100PSensor::init reset failed\n");
+ return false;
+ }
+ delay(20);
+ // Set range
+ if (!setRange(QMA_6100P_MPU_ACCEL_SCALE)) {
+ LOG_WARN("QMA6100PSensor::init range failed");
+ return false;
+ }
+ // set active mode
+ if (!enableAccel()) {
+ LOG_WARN("ERROR :QMA6100PSensor::active mode set failed");
+ }
+ // set calibrateoffsets
+ if (!calibrateOffsets()) {
+ LOG_WARN("ERROR :QMA6100PSensor:: calibration failed");
+ }
+#ifdef QMA_6100P_INT_PIN
+
+ // Active low & Open Drain
+ uint8_t tempVal;
+ if (!readRegisterRegion(SFE_QMA6100P_INTPINT_CONF, &tempVal, 1)) {
+ LOG_WARN("QMA6100PSensor::init failed to read interrupt pin config");
+ return false;
+ }
+
+ tempVal |= 0b00000010; // Active low & Open Drain
+
+ if (!writeRegisterByte(SFE_QMA6100P_INTPINT_CONF, tempVal)) {
+ LOG_WARN("QMA6100PSensor::init failed to write interrupt pin config");
+ return false;
+ }
+
+ // Latch until cleared, all reads clear the latch
+ if (!readRegisterRegion(SFE_QMA6100P_INT_CFG, &tempVal, 1)) {
+ LOG_WARN("QMA6100PSensor::init failed to read interrupt config");
+ return false;
+ }
+
+ tempVal |= 0b10000001; // Latch until cleared, INT_RD_CLR1
+
+ if (!writeRegisterByte(SFE_QMA6100P_INT_CFG, tempVal)) {
+ LOG_WARN("QMA6100PSensor::init failed to write interrupt config");
+ return false;
+ }
+ // Set up an interrupt pin with an internal pullup for active low
+ pinMode(QMA_6100P_INT_PIN, INPUT_PULLUP);
+
+ // Set up an interrupt service routine
+ attachInterrupt(QMA_6100P_INT_PIN, QMA6100PSetInterrupt, FALLING);
+
+#endif
+ return true;
+}
+
+bool QMA6100PSingleton::setWakeOnMotion()
+{
+ // Enable 'Any Motion' interrupt
+ if (!writeRegisterByte(SFE_QMA6100P_INT_EN2, 0b00000111)) {
+ LOG_WARN("QMA6100PSingleton::setWakeOnMotion failed to write interrupt enable");
+ return false;
+ }
+
+ // Set 'Significant Motion' interrupt map to INT1
+ uint8_t tempVal;
+
+ if (!readRegisterRegion(SFE_QMA6100P_INT_MAP1, &tempVal, 1)) {
+ LOG_WARN("QMA6100PSingleton::setWakeOnMotion failed to read interrupt map");
+ return false;
+ }
+
+ sfe_qma6100p_int_map1_bitfield_t int_map1;
+ int_map1.all = tempVal;
+ int_map1.bits.int1_any_mot = 1; // any motion interrupt to INT1
+ tempVal = int_map1.all;
+
+ if (!writeRegisterByte(SFE_QMA6100P_INT_MAP1, tempVal)) {
+ LOG_WARN("QMA6100PSingleton::setWakeOnMotion failed to write interrupt map");
+ return false;
+ }
+
+ // Clear any current interrupts
+ QMA6100P_IRQ = false;
+ return true;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/motion/QMA6100PSensor.h b/src/motion/QMA6100PSensor.h
new file mode 100644
index 0000000000..7ba00149c5
--- /dev/null
+++ b/src/motion/QMA6100PSensor.h
@@ -0,0 +1,63 @@
+#pragma once
+#ifndef _QMA_6100P_SENSOR_H_
+#define _QMA_6100P_SENSOR_H_
+
+#include "MotionSensor.h"
+
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_QMA6100P)
+
+#include
+
+// Set the default accelerometer scale - gpm2, gpm4, gpm8, gpm16
+#ifndef QMA_6100P_MPU_ACCEL_SCALE
+#define QMA_6100P_MPU_ACCEL_SCALE SFE_QMA6100P_RANGE32G
+#endif
+
+// The I2C address of the Accelerometer (if found) from main.cpp
+extern ScanI2C::DeviceAddress accelerometer_found;
+
+// Singleton wrapper for the Sparkfun QMA_6100P_I2C class
+class QMA6100PSingleton : public QMA6100P
+{
+ private:
+ static QMA6100PSingleton *pinstance;
+
+ protected:
+ QMA6100PSingleton();
+ ~QMA6100PSingleton();
+
+ public:
+ // Create a singleton instance (not thread safe)
+ static QMA6100PSingleton *GetInstance();
+
+ // Singletons should not be cloneable.
+ QMA6100PSingleton(QMA6100PSingleton &other) = delete;
+
+ // Singletons should not be assignable.
+ void operator=(const QMA6100PSingleton &) = delete;
+
+ // Initialise the motion sensor singleton for normal operation
+ bool init(ScanI2C::FoundDevice device);
+
+ // Enable Wake on Motion interrupts (sensor must be initialised first)
+ bool setWakeOnMotion();
+};
+
+class QMA6100PSensor : public MotionSensor
+{
+ private:
+ QMA6100PSingleton *sensor = nullptr;
+
+ public:
+ explicit QMA6100PSensor(ScanI2C::FoundDevice foundDevice);
+
+ // Initialise the motion sensor
+ virtual bool init() override;
+
+ // Called each time our sensor gets a chance to run
+ virtual int32_t runOnce() override;
+};
+
+#endif
+
+#endif
\ No newline at end of file
diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 2a2018395f..39d554100b 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -717,7 +717,7 @@ bool MQTT::isPrivateIpAddress(const char address[])
// Even if it's not a valid IP address, we will know it's not a domain.
bool hasColon = false;
int numDots = 0;
- for (int i = 0; i < length; i++) {
+ for (size_t i = 0; i < length; i++) {
if (!isdigit(address[i]) && address[i] != '.' && address[i] != ':') {
return false;
}
diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp
index 033801d774..b1185e2831 100644
--- a/src/platform/esp32/main-esp32.cpp
+++ b/src/platform/esp32/main-esp32.cpp
@@ -51,7 +51,11 @@ void updateBatteryLevel(uint8_t level) {}
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);
+#endif
}
#ifdef HAS_32768HZ
@@ -166,26 +170,6 @@ void esp32Setup()
#endif
}
-#if 0
-// Turn off for now
-
-uint32_t axpDebugRead()
-{
- axp.debugCharging();
- LOG_DEBUG("vbus current %f", axp.getVbusCurrent());
- LOG_DEBUG("charge current %f", axp.getBattChargeCurrent());
- LOG_DEBUG("bat voltage %f", axp.getBattVoltage());
- LOG_DEBUG("batt pct %d", axp.getBattPercentage());
- LOG_DEBUG("is battery connected %d", axp.isBatteryConnect());
- LOG_DEBUG("is USB connected %d", axp.isVBUSPlug());
- LOG_DEBUG("is charging %d", axp.isChargeing());
-
- return 30 * 1000;
-}
-
-Periodic axpDebugOutput(axpDebugRead);
-#endif
-
/// loop code specific to ESP32 targets
void esp32Loop()
{
@@ -263,4 +247,4 @@ void cpuDeepSleep(uint32_t msecToWake)
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery)
-}
\ No newline at end of file
+}
diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index e56016a5b0..78562e47a7 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -11,6 +11,7 @@
#include "PortduinoGlue.h"
#include "linux/gpio/LinuxGPIOPin.h"
#include "yaml-cpp/yaml.h"
+#include
#include
#include