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 #include @@ -100,33 +101,22 @@ void portduinoSetup() settingsStrings[displayspidev] = ""; settingsMap[spiSpeed] = 2000000; settingsMap[ascii_logs] = !isatty(1); + settingsMap[displayPanel] = no_screen; + settingsMap[touchscreenModule] = no_touchscreen; YAML::Node yamlConfig; if (configPath != nullptr) { - std::cout << "Using " << configPath << " as config file" << std::endl; - try { - yamlConfig = YAML::LoadFile(configPath); - } catch (YAML::Exception &e) { - std::cout << "Could not open " << configPath << " because of error: " << e.what() << std::endl; + if (loadConfig(configPath)) { + std::cout << "Using " << configPath << " as config file" << std::endl; + } else { + std::cout << "Unable to use " << configPath << " as config file" << std::endl; 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; - try { - 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) { + } else if (access("/etc/meshtasticd/config.yaml", R_OK) == 0 && loadConfig("/etc/meshtasticd/config.yaml")) { 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 { std::cout << "No 'config.yaml' found, running simulated." << std::endl; settingsMap[maxnodes] = 200; // Default to 200 nodes @@ -136,10 +126,130 @@ void portduinoSetup() 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. randomSeed(time(NULL)); + gpioChipName += std::to_string(settingsMap[gpiochip]); + + for (configNames i : GPIO_lines) { + if (settingsMap.count(i) && settingsMap[i] > max_GPIO) + max_GPIO = settingsMap[i]; + } + + gpioInit(max_GPIO + 1); // Done here so we can inform Portduino how many GPIOs we need. + + // Need to bind all the configured GPIO pins so they're not simulated + // TODO: Can we do this in the for loop above? + // TODO: If one of these fails, we should log and terminate + if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) { + settingsMap[cs] = RADIOLIB_NC; + } + } + if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) { + settingsMap[irq] = RADIOLIB_NC; + } + } + if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) { + settingsMap[busy] = RADIOLIB_NC; + } + } + if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) { + settingsMap[reset] = RADIOLIB_NC; + } + } + if (settingsMap.count(sx126x_ant_sw) > 0 && settingsMap[sx126x_ant_sw] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[sx126x_ant_sw], gpioChipName) != ERRNO_OK) { + settingsMap[sx126x_ant_sw] = RADIOLIB_NC; + } + } + if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) { + settingsMap[user] = RADIOLIB_NC; + } + } + if (settingsMap.count(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) { + settingsMap[rxen] = RADIOLIB_NC; + } + } + if (settingsMap.count(txen) > 0 && settingsMap[txen] != RADIOLIB_NC) { + if (initGPIOPin(settingsMap[txen], gpioChipName) != ERRNO_OK) { + settingsMap[txen] = RADIOLIB_NC; + } + } + + if (settingsMap[displayPanel] != no_screen) { + if (settingsMap[displayCS] > 0) + initGPIOPin(settingsMap[displayCS], gpioChipName); + if (settingsMap[displayDC] > 0) + initGPIOPin(settingsMap[displayDC], gpioChipName); + if (settingsMap[displayBacklight] > 0) + initGPIOPin(settingsMap[displayBacklight], gpioChipName); + if (settingsMap[displayReset] > 0) + initGPIOPin(settingsMap[displayReset], gpioChipName); + } + if (settingsMap[touchscreenModule] != no_touchscreen) { + if (settingsMap[touchscreenCS] > 0) + initGPIOPin(settingsMap[touchscreenCS], gpioChipName); + if (settingsMap[touchscreenIRQ] > 0) + initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName); + } + + if (settingsStrings[spidev] != "") { + SPI.begin(settingsStrings[spidev].c_str()); + } + if (settingsStrings[traceFilename] != "") { + try { + traceFile.open(settingsStrings[traceFilename], std::ios::out | std::ios::app); + } catch (std::ofstream::failure &e) { + std::cout << "*** traceFile Exception " << e.what() << std::endl; + exit(EXIT_FAILURE); + } + } + + return; +} + +int initGPIOPin(int pinNum, const std::string gpioChipName) +{ +#ifdef PORTDUINO_LINUX_HARDWARE + std::string gpio_name = "GPIO" + std::to_string(pinNum); try { + GPIOPin *csPin; + csPin = new LinuxGPIOPin(pinNum, gpioChipName.c_str(), pinNum, gpio_name.c_str()); + csPin->setSilent(); + gpioBind(csPin); + return ERRNO_OK; + } catch (...) { + std::exception_ptr p = std::current_exception(); + std::cout << "Warning, cannot claim pin " << gpio_name << (p ? p.__cxa_exception_type()->name() : "null") << std::endl; + return ERRNO_DISABLED; + } +#else + return ERRNO_OK; +#endif +} + +bool loadConfig(const char *configPath) +{ + YAML::Node yamlConfig; + try { + yamlConfig = YAML::LoadFile(configPath); if (yamlConfig["Logging"]) { if (yamlConfig["Logging"]["LogLevel"].as("info") == "trace") { settingsMap[logoutputlevel] = level_trace; @@ -185,7 +295,6 @@ void portduinoSetup() settingsMap[gpiochip] = yamlConfig["Lora"]["gpiochip"].as(0); settingsMap[ch341Quirk] = yamlConfig["Lora"]["ch341_quirk"].as(false); settingsMap[spiSpeed] = yamlConfig["Lora"]["spiSpeed"].as(2000000); - gpioChipName += std::to_string(settingsMap[gpiochip]); settingsStrings[spidev] = "/dev/" + yamlConfig["Lora"]["spidev"].as("spidev0.0"); if (settingsStrings[spidev].length() == 14) { @@ -211,7 +320,6 @@ void portduinoSetup() if (yamlConfig["I2C"]) { settingsStrings[i2cdev] = yamlConfig["I2C"]["I2CDevice"].as(""); } - settingsMap[displayPanel] = no_screen; if (yamlConfig["Display"]) { if (yamlConfig["Display"]["Panel"].as("") == "ST7789") settingsMap[displayPanel] = st7789; @@ -258,7 +366,6 @@ void portduinoSetup() } } } - settingsMap[touchscreenModule] = no_touchscreen; if (yamlConfig["Touchscreen"]) { if (yamlConfig["Touchscreen"]["Module"].as("") == "XPT2046") settingsMap[touchscreenModule] = xpt2046; @@ -293,113 +400,21 @@ void portduinoSetup() settingsStrings[webserverrootpath] = (yamlConfig["Webserver"]["RootPath"]).as(""); } - settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as(200); - settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as(100); + if (yamlConfig["General"]) { + settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as(200); + settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as(100); + settingsStrings[config_directory] = (yamlConfig["General"]["ConfigDirectory"]).as(""); + } } catch (YAML::Exception &e) { std::cout << "*** Exception " << e.what() << std::endl; - exit(EXIT_FAILURE); - } - - for (configNames i : GPIO_lines) { - if (settingsMap.count(i) && settingsMap[i] > max_GPIO) - max_GPIO = settingsMap[i]; - } - - gpioInit(max_GPIO + 1); // Done here so we can inform Portduino how many GPIOs we need. - - // Need to bind all the configured GPIO pins so they're not simulated - // TODO: Can we do this in the for loop above? - // TODO: If one of these fails, we should log and terminate - if (settingsMap.count(cs) > 0 && settingsMap[cs] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[cs], gpioChipName) != ERRNO_OK) { - settingsMap[cs] = RADIOLIB_NC; - } - } - if (settingsMap.count(irq) > 0 && settingsMap[irq] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[irq], gpioChipName) != ERRNO_OK) { - settingsMap[irq] = RADIOLIB_NC; - } - } - if (settingsMap.count(busy) > 0 && settingsMap[busy] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[busy], gpioChipName) != ERRNO_OK) { - settingsMap[busy] = RADIOLIB_NC; - } - } - if (settingsMap.count(reset) > 0 && settingsMap[reset] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[reset], gpioChipName) != ERRNO_OK) { - settingsMap[reset] = RADIOLIB_NC; - } - } - if (settingsMap.count(sx126x_ant_sw) > 0 && settingsMap[sx126x_ant_sw] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[sx126x_ant_sw], gpioChipName) != ERRNO_OK) { - settingsMap[sx126x_ant_sw] = RADIOLIB_NC; - } - } - if (settingsMap.count(user) > 0 && settingsMap[user] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[user], gpioChipName) != ERRNO_OK) { - settingsMap[user] = RADIOLIB_NC; - } - } - if (settingsMap.count(rxen) > 0 && settingsMap[rxen] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[rxen], gpioChipName) != ERRNO_OK) { - settingsMap[rxen] = RADIOLIB_NC; - } - } - if (settingsMap.count(txen) > 0 && settingsMap[txen] != RADIOLIB_NC) { - if (initGPIOPin(settingsMap[txen], gpioChipName) != ERRNO_OK) { - settingsMap[txen] = RADIOLIB_NC; - } - } - - if (settingsMap[displayPanel] != no_screen) { - if (settingsMap[displayCS] > 0) - initGPIOPin(settingsMap[displayCS], gpioChipName); - if (settingsMap[displayDC] > 0) - initGPIOPin(settingsMap[displayDC], gpioChipName); - if (settingsMap[displayBacklight] > 0) - initGPIOPin(settingsMap[displayBacklight], gpioChipName); - if (settingsMap[displayReset] > 0) - initGPIOPin(settingsMap[displayReset], gpioChipName); - } - if (settingsMap[touchscreenModule] != no_touchscreen) { - if (settingsMap[touchscreenCS] > 0) - initGPIOPin(settingsMap[touchscreenCS], gpioChipName); - if (settingsMap[touchscreenIRQ] > 0) - initGPIOPin(settingsMap[touchscreenIRQ], gpioChipName); - } - - if (settingsStrings[spidev] != "") { - SPI.begin(settingsStrings[spidev].c_str()); + return false; } - if (settingsStrings[traceFilename] != "") { - try { - traceFile.open(settingsStrings[traceFilename], std::ios::out | std::ios::app); - } catch (std::ofstream::failure &e) { - std::cout << "*** traceFile Exception " << e.what() << std::endl; - exit(EXIT_FAILURE); - } - } - - return; + return true; } -int initGPIOPin(int pinNum, const std::string gpioChipName) +// 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) { -#ifdef PORTDUINO_LINUX_HARDWARE - std::string gpio_name = "GPIO" + std::to_string(pinNum); - try { - GPIOPin *csPin; - csPin = new LinuxGPIOPin(pinNum, gpioChipName.c_str(), pinNum, gpio_name.c_str()); - csPin->setSilent(); - gpioBind(csPin); - return ERRNO_OK; - } catch (...) { - std::exception_ptr p = std::current_exception(); - std::cout << "Warning, cannot claim pin " << gpio_name << (p ? p.__cxa_exception_type()->name() : "null") << std::endl; - return ERRNO_DISABLED; - } -#else - return ERRNO_OK; -#endif + return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; } \ No newline at end of file diff --git a/src/platform/portduino/PortduinoGlue.h b/src/platform/portduino/PortduinoGlue.h index 8ee96717e5..95d82c1a21 100644 --- a/src/platform/portduino/PortduinoGlue.h +++ b/src/platform/portduino/PortduinoGlue.h @@ -55,7 +55,8 @@ enum configNames { webserverrootpath, maxtophone, maxnodes, - ascii_logs + ascii_logs, + config_directory }; enum { no_screen, x11, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9488, hx8357d }; 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 settingsMap; extern std::map settingsStrings; extern std::ofstream traceFile; -int initGPIOPin(int pinNum, std::string gpioChipname); \ No newline at end of file +int initGPIOPin(int pinNum, std::string gpioChipname); +bool loadConfig(const char *configPath); +static bool ends_with(std::string_view str, std::string_view suffix); \ No newline at end of file diff --git a/variants/betafpv_2400_tx_micro/variant.h b/variants/betafpv_2400_tx_micro/variant.h index fd06183ee1..67699e7c87 100644 --- a/variants/betafpv_2400_tx_micro/variant.h +++ b/variants/betafpv_2400_tx_micro/variant.h @@ -34,4 +34,4 @@ #define SX128X_TXEN 26 #define SX128X_RXEN 27 #define SX128X_RESET LORA_RESET -#define SX128X_MAX_POWER 13 \ No newline at end of file +#define SX128X_MAX_POWER 3 diff --git a/variants/diy/mesh-tab/variant.h b/variants/diy/mesh-tab/variant.h new file mode 100644 index 0000000000..0a23a3c368 --- /dev/null +++ b/variants/diy/mesh-tab/variant.h @@ -0,0 +1,49 @@ +#ifndef _VARIANT_MESHTAB_DIY_ +#define _VARIANT_MESHTAB_DIY_ + +#define HAS_TOUCHSCREEN 1 + +#define SLEEP_TIME 120 + +// Analog pins +#define BATTERY_PIN 4 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +// ratio of voltage divider = 2.0 +#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage. +#define ADC_CHANNEL ADC1_GPIO4_CHANNEL + +// LED +#define LED_PIN 21 + +// Button +#define BUTTON_PIN 0 + +// GPS +#define GPS_RX_PIN 18 +#define GPS_TX_PIN 17 + +// #define HAS_SDCARD 1 +#define SPI_MOSI 13 +#define SPI_SCK 12 +#define SPI_MISO 11 +#define SPI_CS 10 +#define SDCARD_CS 6 + +// LORA SPI +#define LORA_SCK 36 +#define LORA_MISO 37 +#define LORA_MOSI 35 +#define LORA_CS 39 + +// LORA MODULES +#define USE_SX1262 + +// LORA CONFIG +#define SX126X_CS LORA_CS +#define SX126X_DIO1 15 +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_BUSY 40 +#define SX126X_RESET 14 +#define SX126X_RXEN 47 +#define SX126X_TXEN RADIOLIB_NC // Assuming that DIO2 is connected to TXEN pin + +#endif diff --git a/variants/diy/platformio.ini b/variants/diy/platformio.ini index f3c22b7a87..00ff88da2b 100644 --- a/variants/diy/platformio.ini +++ b/variants/diy/platformio.ini @@ -88,3 +88,73 @@ build_flags = -D ARDUINO_USB_MODE=0 -D ARDUINO_USB_CDC_ON_BOOT=1 -I variants/diy/t-energy-s3_e22 + +; esp32-s3 + ra-sh01 lora + 3.2" ILI9143 +[env:mesh-tab] +extends = esp32s3_base +board = um_feathers3 +board_level = extra +board_upload.flash_size = 16MB +board_build.partitions = default_16MB.csv +upload_protocol = esptool +build_flags = ${esp32s3_base.build_flags} + -D MESH_TAB + -D PRIVATE_HW + -D CONFIG_ARDUHAL_ESP_LOG + -D CONFIG_ARDUHAL_LOG_COLORS=1 + -D CONFIG_DISABLE_HAL_LOCKS=1 ; "feels" to be a bit more stable without locks + -D MESHTASTIC_EXCLUDE_CANNEDMESSAGES=1 + -D MESHTASTIC_EXCLUDE_INPUTBROKER=1 + -D MESHTASTIC_EXCLUDE_BLUETOOTH=1 + -D MESHTASTIC_EXCLUDE_WEBSERVER=1 + -D LV_LVGL_H_INCLUDE_SIMPLE + -D LV_CONF_INCLUDE_SIMPLE + -D LV_COMP_CONF_INCLUDE_SIMPLE + -D LV_USE_SYSMON=0 + -D LV_USE_PROFILER=0 + -D LV_USE_PERF_MONITOR=0 + -D LV_USE_MEM_MONITOR=0 + -D LV_USE_LOG=0 + -D LV_BUILD_TEST=0 + -D USE_LOG_DEBUG + -D LOG_DEBUG_INC=\"DebugConfiguration.h\" + -D RADIOLIB_SPI_PARANOID=0 + -D MAX_NUM_NODES=250 + -D MAX_THREADS=40 + -D HAS_SCREEN=0 + -D HAS_TFT=1 + -D RAM_SIZE=1024 + -D LGFX_DRIVER_TEMPLATE + -D LGFX_DRIVER=LGFX_GENERIC + -D LGFX_PANEL=ILI9341 + -D LGFX_OFFSET_ROTATION=1 + -D LGFX_TOUCH=XPT2046 + -D LGFX_PIN_SCK=12 + -D LGFX_PIN_MOSI=13 + -D LGFX_PIN_MISO=11 + -D LGFX_PIN_DC=16 + -D LGFX_PIN_CS=10 + -D LGFX_PIN_RST=-1 + -D LGFX_PIN_BL=42 + -D LGFX_TOUCH_INT=41 + -D LGFX_TOUCH_CS=7 + -D LGFX_TOUCH_CLK=12 + -D LGFX_TOUCH_DO=11 + -D LGFX_TOUCH_DIN=13 + -D LGFX_TOUCH_X_MIN=300 + -D LGFX_TOUCH_X_MAX=3900 + -D LGFX_TOUCH_Y_MIN=400 + -D LGFX_TOUCH_Y_MAX=3900 + -D VIEW_320x240 + -D USE_PACKET_API + -I lib/device-ui/generated/ui_320x240 + -I variants/diy/mesh-tab +build_src_filter = ${esp32_base.build_src_filter} + +<../lib/device-ui/generated/ui_320x240> + +<../lib/device-ui/resources> + +<../lib/device-ui/locale> + +<../lib/device-ui/source> +lib_deps = ${esp32_base.lib_deps} + lovyan03/LovyanGFX@^1.1.16 + earlephilhower/ESP8266Audio@^1.9.7 + earlephilhower/ESP8266SAM@^1.0.1 diff --git a/variants/icarus/pins_arduino.h b/variants/icarus/pins_arduino.h new file mode 100644 index 0000000000..12d72d6ddd --- /dev/null +++ b/variants/icarus/pins_arduino.h @@ -0,0 +1,21 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x2886 +#define USB_PID 0x0059 + +// GPIO48 Reference: https://github.com/espressif/arduino-esp32/pull/8600 + +// The default Wire will be mapped to Screen and Sensors +static const uint8_t SDA = 8; +static const uint8_t SCL = 9; + +// Default SPI will be mapped to Radio +static const uint8_t MISO = 39; +static const uint8_t SCK = 21; +static const uint8_t MOSI = 38; +static const uint8_t SS = 17; + +#endif /* Pins_Arduino_h */ diff --git a/variants/icarus/platformio.ini b/variants/icarus/platformio.ini new file mode 100644 index 0000000000..11f09cab41 --- /dev/null +++ b/variants/icarus/platformio.ini @@ -0,0 +1,19 @@ +[env:icarus] +extends = esp32s3_base +board = icarus +board_level = extra +board_check = true +board_build.mcu = esp32s3 +upload_protocol = esptool +upload_speed = 921600 +platform_packages = framework-arduinoespressif32@https://github.com/PowerFeather/powerfeather-meshtastic-arduino-lib/releases/download/2.0.16a/esp32-2.0.16.zip +lib_deps = + ${esp32s3_base.lib_deps} +build_unflags = + ${esp32s3_base.build_unflags} + -DARDUINO_USB_MODE=1 +build_flags = + ${esp32s3_base.build_flags} -D PRIVATE_HW -I variants/icarus + -DBOARD_HAS_PSRAM + + -DARDUINO_USB_MODE=0 diff --git a/variants/icarus/variant.h b/variants/icarus/variant.h new file mode 100644 index 0000000000..c9c74b45a5 --- /dev/null +++ b/variants/icarus/variant.h @@ -0,0 +1,31 @@ +// Icarus has a 1.3 inch OLED Screen +#define SCREEN_SSD106 + +#define I2C_SDA 8 +#define I2C_SCL 9 + +#define I2C_SDA1 18 +#define I2C_SCL1 6 + +#define BUTTON_PIN 7 // Selection button + +// RA-01SH/HT-RA62 LORA module +#define USE_SX1262 + +#define LORA_MISO 39 +#define LORA_SCK 21 +#define LORA_MOSI 38 +#define LORA_CS 17 + +#define LORA_RESET 42 +#define LORA_DIO1 5 + +#ifdef USE_SX1262 +#define SX126X_CS LORA_CS +#define SX126X_DIO1 LORA_DIO1 +#define SX126X_BUSY 47 +#define SX126X_RESET LORA_RESET + +// DIO2 controlls an antenna switch +#define SX126X_DIO2_AS_RF_SWITCH +#endif diff --git a/variants/monteops_hw1/platformio.ini b/variants/monteops_hw1/platformio.ini index 4b42dce397..eaa246526a 100644 --- a/variants/monteops_hw1/platformio.ini +++ b/variants/monteops_hw1/platformio.ini @@ -12,4 +12,4 @@ lib_deps = https://github.com/RAKWireless/RAK13800-W5100S.git#1.0.2 debug_tool = jlink ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) -;upload_protocol = jlink \ No newline at end of file +;upload_protocol = jlink diff --git a/variants/tracker-t1000-e/platformio.ini b/variants/tracker-t1000-e/platformio.ini index dfc72f3f03..0758116100 100644 --- a/variants/tracker-t1000-e/platformio.ini +++ b/variants/tracker-t1000-e/platformio.ini @@ -1,16 +1,14 @@ -; tracker-t1000-e v0.9.1 [env:tracker-t1000-e] extends = nrf52840_base board = tracker-t1000-e -; board_level = extra -; platform = https://github.com/maxgerhardt/platform-nordicnrf52#cac6fcf943a41accd2aeb4f3659ae297a73f422e build_flags = ${nrf52840_base.build_flags} -Ivariants/tracker-t1000-e -Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52 -DTRACKER_T1000_E -L "${platformio.libdeps_dir}/${this.__env__}/bsec2/src/cortex-m4/fpv4-sp-d16-hard" - -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely. + -DGPS_POWER_TOGGLE board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld build_src_filter = ${nrf52_base.build_src_filter} +<../variants/tracker-t1000-e> lib_deps = ${nrf52840_base.lib_deps} + https://github.com/meshtastic/QMA6100P_Arduino_Library.git#14c900b8b2e4feaac5007a7e41e0c1b7f0841136 debug_tool = jlink ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) upload_protocol = nrfutil diff --git a/variants/tracker-t1000-e/variant.cpp b/variants/tracker-t1000-e/variant.cpp index 85e0c44f39..8096705d02 100644 --- a/variants/tracker-t1000-e/variant.cpp +++ b/variants/tracker-t1000-e/variant.cpp @@ -40,7 +40,7 @@ void initVariant() digitalWrite(PIN_3V3_EN, HIGH); pinMode(PIN_3V3_ACC_EN, OUTPUT); - digitalWrite(PIN_3V3_ACC_EN, LOW); + digitalWrite(PIN_3V3_ACC_EN, HIGH); pinMode(BUZZER_EN_PIN, OUTPUT); digitalWrite(BUZZER_EN_PIN, HIGH); diff --git a/variants/tracker-t1000-e/variant.h b/variants/tracker-t1000-e/variant.h index b8eb59b318..6a1f996002 100644 --- a/variants/tracker-t1000-e/variant.h +++ b/variants/tracker-t1000-e/variant.h @@ -52,7 +52,7 @@ extern "C" { #define LED_BLUE -1 // Actually green #define LED_STATE_ON 1 // State when LED is lit -#define BUTTON_PIN (0 + 6) // P0.6 +#define BUTTON_PIN (0 + 6) // P0.06 #define BUTTON_ACTIVE_LOW false #define BUTTON_ACTIVE_PULLUP false #define BUTTON_SENSE_TYPE 0x6 @@ -61,9 +61,11 @@ extern "C" { #define WIRE_INTERFACES_COUNT 1 -// unused pins -#define PIN_WIRE_SDA (0 + 9) // P0.26 -#define PIN_WIRE_SCL (0 + 10) // P0.27 +#define PIN_WIRE_SDA (0 + 26) // P0.26 +#define PIN_WIRE_SCL (0 + 27) // P0.27 +#define I2C_NO_RESCAN // I2C is a bit finicky, don't scan too much +#define HAS_QMA6100P // very rare beast, only on this board. +#define QMA_6100P_INT_PIN (32 + 2) // P1.02 /* * Serial interfaces @@ -116,14 +118,22 @@ extern "C" { #define PIN_GPS_RESET (32 + 15) // P1.15 #define GPS_RESET_MODE HIGH -#define GPS_VRTC_EN (0 + 8) // P0.8, awlays high -#define GPS_SLEEP_INT (32 + 12) // P1.12, awlays high +#define GPS_VRTC_EN (0 + 8) // P0.8, always high +#define GPS_SLEEP_INT (32 + 12) // P1.12, always high #define GPS_RTC_INT (0 + 15) // P0.15, normal is LOW, wake by HIGH -#define GPS_RESETB_OUT (32 + 14) // P1.14, awlays input pull_up +#define GPS_RESETB_OUT (32 + 14) // P1.14, always input pull_up #define GPS_FIX_HOLD_TIME 15000 // ms -#define BATTERY_PIN 2 +#define BATTERY_PIN 2 // P0.02/AIN0, BAT_ADC +#define BATTERY_IMMUTABLE #define ADC_MULTIPLIER (2.0F) +// P0.04/AIN2 is VCC_ADC, P0.05/AIN3 is CHARGER_DET, P1.03 is CHARGE_STA, P1.04 is CHARGE_DONE + +#define EXT_CHRG_DETECT (32 + 3) // P1.03 +#define EXT_CHRG_DETECT_VALUE LOW +// #define EXT_IS_CHRGD (32 + 4) // P1.04 +// #define EXT_IS_CHRGD_VALUE LOW +#define EXT_PWR_DETECT (0 + 5) // P0.05 #define ADC_RESOLUTION 14 #define BATTERY_SENSE_RESOLUTION_BITS 12 @@ -133,13 +143,13 @@ extern "C" { #define VBAT_AR_INTERNAL AR_INTERNAL_3_0 // Buzzer -#define BUZZER_EN_PIN (32 + 5) // P1.05, awlays high +#define BUZZER_EN_PIN (32 + 5) // P1.05, always high #define PIN_BUZZER (0 + 25) // P0.25, pwm output #define T1000X_SENSOR_EN #define T1000X_VCC_PIN (0 + 4) // P0.4 -#define T1000X_NTC_PIN (0 + 31) // P0.31 -#define T1000X_LUX_PIN (0 + 29) // P0.29 +#define T1000X_NTC_PIN (0 + 31) // P0.31/AIN7 +#define T1000X_LUX_PIN (0 + 29) // P0.29/AIN5 #ifdef __cplusplus } diff --git a/variants/unphone/pins_arduino.h b/variants/unphone/pins_arduino.h new file mode 100644 index 0000000000..c4e9add1c3 --- /dev/null +++ b/variants/unphone/pins_arduino.h @@ -0,0 +1,67 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x16D0 +#define USB_PID 0x1178 + +#define EXTERNAL_NUM_INTERRUPTS 46 +#define NUM_DIGITAL_PINS 48 +#define NUM_ANALOG_INPUTS 20 + +#define analogInputToDigitalPin(p) (((p) < 20) ? (analogChannelToDigitalPin(p)) : -1) +#define digitalPinToInterrupt(p) (((p) < 48) ? (p) : -1) +#define digitalPinHasPWM(p) (p < 46) + +#define LED_BUILTIN 13 +#define BUILTIN_LED LED_BUILTIN // backward compatibility + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = 3; +static const uint8_t SCL = 4; + +static const uint8_t SS = 13; +static const uint8_t MOSI = 40; +static const uint8_t MISO = 41; +static const uint8_t SCK = 39; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 8; +static const uint8_t A3 = 9; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; +static const uint8_t A6 = 14; +static const uint8_t A7 = 7; +static const uint8_t A8 = 15; +static const uint8_t A9 = 33; +static const uint8_t A10 = 27; +static const uint8_t A11 = 12; +static const uint8_t A12 = 13; +static const uint8_t A13 = 14; +static const uint8_t A14 = 15; +static const uint8_t A15 = 16; +static const uint8_t A16 = 17; +static const uint8_t A17 = 18; +static const uint8_t A18 = 19; +static const uint8_t A19 = 20; + +static const uint8_t T1 = 2; +static const uint8_t T2 = 8; +static const uint8_t T3 = 9; +static const uint8_t T4 = 5; +static const uint8_t T5 = 6; +static const uint8_t T6 = 14; +static const uint8_t T7 = 7; +static const uint8_t T8 = 15; +static const uint8_t T9 = 33; +static const uint8_t T10 = 27; +static const uint8_t T11 = 12; +static const uint8_t T12 = 13; +static const uint8_t T13 = 14; +static const uint8_t T14 = 15; + +#endif /* Pins_Arduino_h */ diff --git a/variants/unphone/platformio.ini b/variants/unphone/platformio.ini index dbfa0599d7..489c70f998 100644 --- a/variants/unphone/platformio.ini +++ b/variants/unphone/platformio.ini @@ -13,7 +13,6 @@ build_unflags = -D ARDUINO_USB_MODE build_flags = ${esp32_base.build_flags} - ;-D BOARD_HAS_PSRAM // what's up with this - doesn't seem to be recognised at boot -D UNPHONE -I variants/unphone -D ARDUINO_USB_MODE=0 @@ -27,6 +26,52 @@ build_flags = ${esp32_base.build_flags} build_src_filter = ${esp32_base.build_src_filter} +<../variants/unphone> lib_deps = ${esp32s3_base.lib_deps} - lovyan03/LovyanGFX @ ^1.1.8 - https://gitlab.com/hamishcunningham/unphonelibrary#meshtastic @ ^9.0.0 - adafruit/Adafruit NeoPixel @ ^1.12.0 \ No newline at end of file + lovyan03/LovyanGFX@ 1.1.12 + https://gitlab.com/hamishcunningham/unphonelibrary#meshtastic@9.0.0 + adafruit/Adafruit NeoPixel @ ^1.12.0 + + +[env:unphone-tft] +extends = esp32s3_base +board_level = extra +board = unphone +board_build.partitions = default_8MB.csv +monitor_speed = 115200 +monitor_filters = esp32_exception_decoder +build_flags = ${esp32_base.build_flags} + -D UNPHONE + -D UNPHONE_ACCEL=0 + -D UNPHONE_TOUCHS=0 + -D UNPHONE_SDCARD=0 + -D UNPHONE_UI0=0 + -D UNPHONE_LORA=0 + -D UNPHONE_FACTORY_MODE=0 + -D MAX_THREADS=40 + -D HAS_SCREEN=0 + -D HAS_TFT=1 + -D RAM_SIZE=512 + -D LV_LVGL_H_INCLUDE_SIMPLE + -D LV_CONF_INCLUDE_SIMPLE + -D LV_COMP_CONF_INCLUDE_SIMPLE + -D LV_BUILD_TEST=0 + -D LV_USE_PERF_MONITOR=0 + -D LV_USE_MEM_MONITOR=0 + -D USE_LOG_DEBUG + -D LOG_DEBUG_INC=\"DebugConfiguration.h\" +; -D CALIBRATE_TOUCH=0 + -D LGFX_DRIVER=LGFX_UNPHONE_V9 + -D VIEW_320x240 +; -D USE_DOUBLE_BUFFER + -D USE_PACKET_API + -I lib/device-ui/generated/ui_320x240 + -I variants/unphone + +build_src_filter = ${esp32_base.build_src_filter} +<../variants/unphone> + +<../lib/device-ui/generated/ui_320x240> + +<../lib/device-ui/resources> + +<../lib/device-ui/source> + +lib_deps = ${esp32s3_base.lib_deps} + lovyan03/LovyanGFX@^1.1.12 + https://gitlab.com/hamishcunningham/unphonelibrary#meshtastic@9.0.0 + adafruit/Adafruit NeoPixel@1.12.0 \ No newline at end of file diff --git a/version.properties b/version.properties index 327f294430..b9deaacb27 100644 --- a/version.properties +++ b/version.properties @@ -1,4 +1,4 @@ [VERSION] major = 2 minor = 5 -build = 8 +build = 10