Compare commits

..

51 Commits

Author SHA1 Message Date
Ben Meadors
cf574c71d8 Fix build 2025-07-18 09:24:34 -05:00
Tom Fifield
abe0a34fc0 Add additional Epoch check for time set (#7375)
We have two perhapsSetRTC functions, which are called to set the time.

The one with 3 parameters had a helpful check to reject an invalid time,
by comparing the time from the source against when the firmware was compiled.
The one with 2 parameters, which is called from the GPS lookForTime did not.

As a result, certain GPS with bad time handling could set a time that was
in the past.

This patch adds the same epoch check code to the other perhapsSetRTC method.

Fixes https://github.com/meshtastic/firmware/issues/7364
2025-07-18 05:49:19 -05:00
Jason P
71b6508ad3 BaseUI Updates (#7358)
* Calculate the length of the right string and use it

* Improve readability of Version Number

* Prevent negative message IDs and proactively favorite DM'd nodes

* Patch up Remove Favorite functionality

* Fix warnings for TFT_MESH_* and hasSupportBrightness

* Fix warning around casting variables

* Correct Favorite Node Behavior to rebuild favorite nodes when updated.

* Resolve bool kb_found issue not working for second discovery keyboards

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-07-16 19:44:23 -05:00
Thomas Göttgens
55fc4fcd90 clean up double i2c init/scan code (#7359) 2025-07-17 00:40:29 +02:00
Jonathan Bennett
c3b2b474c6 Drop NodeInfo packets if the is_licensed bit doesn't match owner (#7361) 2025-07-16 16:05:34 -05:00
Ben Meadors
39716ed1ba Fix L1 EInk HWModel (#7346) 2025-07-14 21:14:07 -05:00
Jason P
625a529f6c Message frame New Message Options and Clock / TDeck / Brightness Refinements (#7344)
* Clock updates and some TDeck corrections

* TDeck Brightness Works in TFT Builds

* Remove HAS_TFT from enabling Brightness, disable Brightness for TDeck

* Add default textMessage frame actions and adjust SharedUIDisplay

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-14 20:59:22 -05:00
Ben Meadors
31d56c16d5 Map report should work over devices which do not have network hardware (with client proxy) (#7341)
* Map report should work over devices which do not have network hardware (with client proxy)

* Fix else
2025-07-14 20:13:34 -05:00
Austin
5776385e8c STM32 PlatformIO cleanup (#7339) 2025-07-14 12:52:21 -05:00
github-actions[bot]
8f10de5684 [create-pull-request] automated change (#7338)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-07-14 09:46:43 -05:00
Daniel.Cao
e864fcf9a8 feat: add support for RAK Wismesh Tag hardware platform (#6853)
* add new platform rak_wismeshtag
* Remove RTC and Ethernet definitions from variant.h
* Remove unused EINK and Ethernet definitions from variant.h and platformio.ini
* Add WISMESH_TAG hardware model definition in architecture.h and update build flags in platformio.ini
* Remove unused build flags and dependencies
---------
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: daniel <daniel.cao@rakwireless.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
2025-07-14 16:29:42 +02:00
Ben Meadors
86af5f5252 Trunk 2025-07-14 05:44:29 -05:00
Quency-D
daa1d582cb The screen display of the heltec wireless tracker is abnormal. (#7337)
The screen of the heltec wireless tracker uses the same power source as the GPS. If the GPS turns off the power during the screen shutdown period and then turns on the power, the screen will not function properly. So initialize the screen every time it starts.
2025-07-14 05:43:25 -05:00
github-actions[bot]
f197f0e5ec Upgrade trunk (#7336)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-07-14 05:12:52 -05:00
Chloe Bethel
3599ca6845 Add heap info via standard mallinfo() function for STM32 (#7327)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-14 05:12:38 -05:00
Chloe Bethel
1be4fc5ae9 GPS for STM32WL (#7297)
* Enable GPS for Wio-E5 variant on Serial2

* Add ability to override GPS serial port using GPS_SERIAL_PORT, and make RAK2560 use it.

* Don't try to send ATAK packets if ATAK is disabled, +4k flash
2025-07-14 05:12:26 -05:00
Thomas Göttgens
ac3e5684d6 get git url part from local repo (#7331) 2025-07-14 05:11:26 -05:00
Austin
29cca4d621 Revert "Actions: Move all Linux packaging into subdir (#7332)" (#7334)
This reverts commit f3ff80963a.
2025-07-13 20:54:52 -04:00
Austin
f3ff80963a Actions: Move all Linux packaging into subdir (#7332) 2025-07-13 20:48:17 -04:00
Austin
45e428eb25 PPA: Add Ubuntu Questing (25.10) to daily builds (#7329) 2025-07-13 16:22:42 -04:00
Thomas Göttgens
16d2650236 add pioenv to version string in debug log (#7328) 2025-07-13 19:16:14 +02:00
TSAO
2ecbf704d0 Improve OLED UI Responsiveness and Force Redraws for Canned message module (#7324)
* No delay between UI frame rendering for OLED

* force redraw the display

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jason P <applewiz@mac.com>
2025-07-13 08:28:05 -05:00
Styne13
5e28ee6d1e NodeDB.cpp: Fix iOS bluetooth crash by ensuring UINT32_MAX is not used (#7312)
Signed-off-by: Marcel <6253936+Styne13@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-13 06:26:35 -05:00
Neil Hanlon
622023de8b fix(device-update.sh): safely filter args without breaking parsing (#7305)
The previous method of removing `--change-mode` from the argument list used a
string (`NEW_ARGS`) and `eval set -- $NEW_ARGS` to reconstruct the positional
parameters. This was both unsafe and incorrect.

Because `NEW_ARGS` was built using quoted literal `$arg` strings instead of the
actual values, it resulted in all filtered arguments being set to the same last
value of `$arg`. This caused `getopts` to receive incorrect input and silently
fail to parse options like `-p` and `-f`, leading to broken behavior and unset
variables (e.g., `ESPTOOL_CMD` never got a port).

This patch rewrites the logic to use an array (`NEW_ARGS+=("$arg")`), and
resets positional parameters via `set -- "${NEW_ARGS[@]}"`. This preserves
argument integrity and avoids the unsafe use of `eval`.

Example of the broken behavior before this fix:

  ./device-update.sh -p /dev/ttyACM0 -f firmware.bin

Resulted in:

  set -- firmware.bin firmware.bin firmware.bin firmware.bin

Now:

  set -- -p /dev/ttyACM0 -f firmware.bin

as expected.

Signed-off-by: Neil Hanlon <neil@shrug.pw>
2025-07-13 06:19:58 -05:00
Andrew Yong
b49e59b904 xiao_ble README.md updates (#7283)
* docs(xiao_ble): Simplify building and flashing instructions

- **Update Bootloader** - deleted this section, as Meshtastic now builds-in a compatible SoftDevice
- **PlatformIO Environment Preparation** - deleted this section, as Meshtastic now builds-in a compatible SoftDevice
- **Build Meshtastic** - simplified it greatly by referring to Meshtastic documentation
- **Flash the firmware to the Xiao BLE** - simplified it greatly as Meshtastic now builds firmware.uf2; added some observations for a succesful flash

Light cleanup of Markdown and renumbering of sections.

Signed-off-by: Andrew Yong <me@ndoo.sg>

* docs(xiao_ble): Replace some HTML with Markdown, cleanup Markdown

Signed-off-by: Andrew Yong <me@ndoo.sg>

* docs(xiao_ble): Update SX126X_TXEN definition location

Signed-off-by: Andrew Yong <me@ndoo.sg>

* docs(xiao_ble): Fresher information about E22 modules

Signed-off-by: Andrew Yong <me@ndoo.sg>

* docs(xiao_ble): Instructions for E22...M33S modules

Also re-order the Build section to come after the Wiring section since the build instructions require special attention if the wiring/modules differ from the variant's expected pins/module.

Signed-off-by: Andrew Yong <me@ndoo.sg>

* docs(xiao_ble): Rename all XIAO BLE to XIAO nRF52840

Signed-off-by: Andrew Yong <me@ndoo.sg>

* docs(xiao_ble): Remove note about Linux since shell script is gone

Signed-off-by: Andrew Yong <me@ndoo.sg>

* docs(xiao_ble): trunk fmt and fix links

Signed-off-by: Andrew Yong <me@ndoo.sg>

---------

Signed-off-by: Andrew Yong <me@ndoo.sg>
2025-07-13 06:17:50 -05:00
Andrew Yong
0133c5dc9e feat: New variant esp32c3_super_mini (#7133)
https://www.espboards.dev/esp32/esp32-c3-super-mini/

DIY build by @AntonKartajaya on Meshtastic Discord and a PCB version WIP by https://github.com/NomDeTom.

- I2C
  - I2C_SDA: 1
  - I2C_SCL: 0
  - OLED: SSD1306
- GPS
  - GPS_RX_PIN: 20
  - GPS_TX_PIN: 21
- Button
  - BUTTON_PIN: 9
- SPI
  - SCK: 10
  - MISO: 6
  - MOSI: 7
  - CS: 8
- LoRa: SX1262
  - LORA_RESET: 5
  - LORA_DIO1: 3
  - LORA_RXEN: 2
  - LORA_BUSY: 4

Signed-off-by: Andrew Yong <me@ndoo.sg>
2025-07-13 06:12:24 -05:00
Andrew Yong
fd414ed149 feat: DIY Seeed XIAO nRF52840 + EBYTE E22 variants, pin-compatible with Wio-SX1262 kit (#7105)
These DIY builds are functionally similar to the legacy xiao_ble variant, but use a pinout harmonized with the officially-supported XIAO nRF52840 & Wio-SX1262 Kit for Meshtastic (SKU 102010710).

An additional E22-900M33S variant is provided to ensure SX1262 transmit power is set below the maximum PA input for that module, to avoid damaging it.

- seeed_xiao_nrf52840_e22_900m30s:
  - XIAO nRF52840 + EBYTE E22-900M30S
  - EBYTE E22 pinout matching Wio-SX1262 (SKU 113010003)
  - I2C
    - SDA: D6
    - SCL: D7
  - User button: D0
  - Gain is programmed in firmware - user Tx power setting is the desired final output power
- seeed_xiao_nrf52840_e22_900m33s:
  - XIAO nRF52840 + EBYTE E22-900M33S
  - EBYTE E22 pinout matching Wio-SX1262 (SKU 113010003)
  - I2C
    - SDA: D6
    - SCL: D7
  - User button: D0
  - Gain is programmed in firmware - user Tx power setting is the desired final output power

Signed-off-by: Andrew Yong <me@ndoo.sg>
2025-07-13 15:58:01 +08:00
Austin
77768e9023 Remove Ubuntu oracular (#7322) 2025-07-13 12:39:20 +08:00
Austin
86be2ac12f userPrefs: Set default ringtone nag time (#7314) 2025-07-12 16:26:25 -05:00
Jonathan Bennett
4342d51f5a Bump Framework-native and set version string. (#7317) 2025-07-12 14:44:58 -05:00
Austin
41f52a6566 Build: Update platformio with pkg install (#7315) 2025-07-12 14:35:57 -05:00
Jonathan Bennett
cb47325f08 Seesaw Rotary (#7310)
* Initial add of Adafruit seesaw encoder

* Fully wire up seesaw

* Trunk

* Add #include configuration.h back to unbreak logging

* Tryfix the dumb compilation error

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-12 12:36:44 -05:00
Austin
deed6cd96a STM32: Properly ignore OneButton (#7311) 2025-07-12 06:27:45 -05:00
renovate[bot]
05c32c99e4 Update meshtastic/device-ui digest to 86a09a7 (#7308)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-11 18:46:29 -05:00
Jonathan Bennett
1ca0584ba0 Add first config override for Native (#7306) 2025-07-11 16:09:59 -05:00
Ben Meadors
5ae8021aa6 I'm dumb 2025-07-11 08:28:21 -05:00
Ben Meadors
9798a91e7b Delete ringtone.proto file for factory reset (#7303) 2025-07-11 08:22:50 -05:00
Austin
e9a551ae90 Load ringtone from userPrefs (#7298)
* Load ringtone from userPrefs

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-11 08:09:46 -05:00
Mictronics
d42bde135f Support native configuration Waveshare Pico LoRa module on Orange Pi Zero3 (#7295)
* Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28

* Merge PR #420

* Fixed double and missing Default class.

* Use correct format specifier and fixed typo.

* Removed duplicate code.

* Fix error: #if with no expression

* Fix warning: extra tokens at end of #endif directive.

* Fix antenna switching logic. Complementary-pin control logic is required on the rp2040-lora board.

* Fix deprecated macros.

* Set RP2040 in dormant mode when deep sleep is triggered.

* Fix array out of bounds read.

* Admin key count needs to be set otherwise the key will be zero loaded after reset.

* Don't reset the admin key size when loading defaults. Preserve an existing key in config if possible.

* Remove log spam when reading INA voltage sensor.

* Remove static declaration for admin keys from userPrefs.h. Load hard coded admin keys in case config file has empty slots.

* Removed newlines from log.

* Fix issue #5665.

* Fix build for Pico2 RP2350 platform.

* Enable Wifi client on Pico2W.

* Use correct processor on Pico2.

* Fix deprecated warning.

* Update platform and framework for RP2350.

* Added Pico2W variant including Wifi support.

* Fix typo in used variant.

* Remove obsolete define.

* Fix for native Linux build.

* Simplify RP2350 platform tag reference.

Co-authored-by: Austin <vidplace7@gmail.com>

* Cast user prefs strings.

* Update to last successfully building platform package.

* Define I2C GPIOs to ensure usage of both ports. Possibly fixes #5361

* RAK11310 support for RAK12002 RTC added.

* Update platform and framework packages to 4.4.3.

* Use RP2040 base platform and framework package. Use RAK11300 board definition in arduino-pico framework.

* Use RAK11300 board definition in arduino-pico framework.

* Fix build when MESHTASTIC_EXCLUDE_GPS is defined.

* Added configuration for Waveshare Pico LoRa module in combination with Orange Pi Zero3.

* Equal to upstream master.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com>
Co-authored-by: Austin <vidplace7@gmail.com>
Co-authored-by: Tom Fifield <tom@tomfifield.net>
2025-07-11 06:54:37 -05:00
github-actions[bot]
72f3d19d5a Upgrade trunk (#7278)
Co-authored-by: sachaw <11172820+sachaw@users.noreply.github.com>
2025-07-11 06:51:33 -05:00
renovate[bot]
f7ecf141b5 Update meshtastic/device-ui digest to 404c6e0 (#7302)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-11 06:45:18 -05:00
todd-herbert
1063ef9034 Shorter audio feedback for InkHUD buttons (#7301) 2025-07-11 17:30:48 +12:00
Jonathan Bennett
13ac182142 Pick up nodedb.h in Screen.cpp regardless of HAS_SCREEN state 2025-07-10 23:19:58 -05:00
Jonathan Bennett
4bab148e3b Make the shouldWake function always available, and remove the bool (#7300) 2025-07-10 22:51:43 -05:00
Jason P
be75f11156 Update Screen Wake Default Behavior (#7282)
* feat(display): enable screen wake on received messages

* feat(menu): add Screen Wakeup option in system menu

* feat(ui): update wake on message configuration and refactor save logic

* feat(TextMessageModule): conditionally trigger screen wake on received message

* Refactoring system menu options for notification and screen.

* Fix MUI options in the system menu.

* Build out Reboot/Shutdown Menu and consolidate options within it

* Trunk fixes

* Protobuf ref

* Revert generated files

* Update plumbing for screen_wakeup_menu

* Begin work on crafting a method to stop screen wake for received messages

* SharedUIDisplay.cpp doesn't need ExternalNotificationModule.h

* Stop screen wake if External Notification is enabled

* Removing extra log lines

* Add role and battery state checks for not waking screen. Menu updates to resolve some Back options not being linked

* Resolve some additional merge conflict related issues

* Shouldn't throttle the power menu

* Finalize renames of some menus

* Flip Flop MUI Menu to avoid accidental clicks

* NULL check for powerStatus

* Remove "Wakeup" eNum

* Update src/graphics/Screen.cpp

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* CoPilot was close this should fix the builds

---------

Co-authored-by: whywilson <m.tools@qq.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-10 19:49:15 -05:00
renovate[bot]
093868f3ed Update dorny/test-reporter action to v2.1.1 (#7284)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-10 17:12:39 -05:00
renovate[bot]
fe534eae37 Update Adafruit BusIO to v1.17.2 (#7277)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-10 17:12:25 -05:00
Kongduino
1aad442ccc Update platformio.ini (#7289)
The link to the product should point to the vendor's website, not a random distributor.

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-07-10 17:11:19 -05:00
renovate[bot]
57c1c9286b Update RadioLib to v7.2.1 (#7287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-10 17:11:00 -05:00
Jonathan Bennett
6030bf50e0 Unbreak the macro 2025-07-10 11:40:02 -05:00
github-actions[bot]
6d8c815558 automated bumps (#7293)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-07-10 11:31:40 -05:00
74 changed files with 1273 additions and 456 deletions

View File

@@ -30,7 +30,11 @@ jobs:
strategy:
fail-fast: false
matrix:
series: [plucky, oracular, noble, jammy]
series:
- jammy # 22.04
- noble # 24.04
- plucky # 25.04
- questing # 25.10
uses: ./.github/workflows/package_ppa.yml
with:
ppa_repo: ppa:meshtastic/daily

View File

@@ -20,7 +20,11 @@ jobs:
strategy:
fail-fast: false
matrix:
series: [plucky, oracular, noble, jammy]
series:
- jammy # 22.04
- noble # 24.04
- plucky # 25.04
# - questing # 25.10
uses: ./.github/workflows/package_ppa.yml
with:
ppa_repo: |-

View File

@@ -143,7 +143,7 @@ jobs:
merge-multiple: true
- name: Test Report
uses: dorny/test-reporter@v2.1.0
uses: dorny/test-reporter@v2.1.1
with:
name: PlatformIO Tests
path: testreport.xml

View File

@@ -8,8 +8,8 @@ plugins:
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- checkov@3.2.447
- renovate@41.23.4
- checkov@3.2.450
- renovate@41.30.5
- prettier@3.6.2
- trufflehog@3.89.2
- yamllint@1.37.1

View File

@@ -2,7 +2,7 @@
[portduino_base]
platform =
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
https://github.com/meshtastic/platform-native/archive/681ee029207e9fd040afa223df6e54074cbbe084.zip
https://github.com/meshtastic/platform-native/archive/6cb7a455b440dd0738e8ed74a18136ed5cf7ea63.zip
framework = arduino
build_src_filter =
@@ -30,6 +30,8 @@ lib_deps =
lovyan03/LovyanGFX@^1.2.0
# renovate: datasource=git-refs depName=libch341-spi-userspace packageName=https://github.com/pine64/libch341-spi-userspace gitBranch=main
https://github.com/pine64/libch341-spi-userspace/archive/af9bc27c9c30fa90772279925b7c5913dff789b4.zip
# renovate: datasource=custom.pio depName=adafruit/Adafruit seesaw Library packageName=adafruit/library/Adafruit seesaw Library
adafruit/Adafruit seesaw Library@1.7.9
build_flags =
${arduino_base.build_flags}
@@ -48,4 +50,7 @@ build_flags =
-std=gnu17
-std=c++17
lib_ignore = Adafruit NeoPixel
lib_ignore =
Adafruit NeoPixel
Adafruit ST7735 and ST7789 Library
SD

View File

@@ -23,14 +23,20 @@ build_flags =
-DMESHTASTIC_EXCLUDE_SCREEN=1
-DMESHTASTIC_EXCLUDE_MQTT=1
-DMESHTASTIC_EXCLUDE_BLUETOOTH=1
-DMESHTASTIC_EXCLUDE_GPS=1
-DMESHTASTIC_EXCLUDE_WIFI=1
-DMESHTASTIC_EXCLUDE_TZ=1 ; Exclude TZ to save some flash space.
-DSERIAL_RX_BUFFER_SIZE=256 ; For GPS - the default of 64 is too small.
-DHAS_SCREEN=0 ; Always disable screen for STM32, it is not supported.
-DPIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF ; This is REQUIRED for at least traceroute debug prints - without it the length ends up uninitialized.
;-DDEBUG_MUTE
-DDEBUG_MUTE ; You can #undef DEBUG_MUTE in certain source files if you need the logs.
-fmerge-all-constants
-ffunction-sections
-fdata-sections
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
-DHAL_DAC_MODULE_ONLY
-DHAL_RNG_MODULE_ENABLED
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/RemoteHardwareModule.cpp> -<platform/nrf52> -<platform/portduino> -<platform/rp2xx0> -<mesh/raspihttp>
@@ -47,4 +53,4 @@ lib_deps =
https://github.com/caveman99/Crypto/archive/eae9c768054118a9399690f8af202853d1ae8516.zip
lib_ignore =
mathertel/OneButton@2.6.1
OneButton

View File

@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
platformio pkg update -e $1
platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*

View File

@@ -25,7 +25,7 @@ mkdir -p $OUTDIR/
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
pio pkg update --environment "$PIO_ENV" || platformioFailed
pio pkg install --environment "$PIO_ENV" || platformioFailed
pio run --environment "$PIO_ENV" || platformioFailed
cp ".pio/build/$PIO_ENV/program" "$OUTDIR/meshtasticd_linux_$(uname -m)"
cp bin/native-install.* $OUTDIR

View File

@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
platformio pkg update -e $1
platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*

View File

@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
platformio pkg update -e $1
platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*

View File

@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
platformio pkg update -e $1
platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*

View File

@@ -199,6 +199,10 @@ HostMetrics:
# UserStringCommand: cat /sys/firmware/devicetree/base/serial-number # Command to execute, to send the results as the userString
Config:
# DisplayMode: TWOCOLOR # uncomment to force BaseUI
# DisplayMode: COLOR # uncomment to force MUI
General:
MaxNodes: 200
MaxMessageQueue: 100

View File

@@ -0,0 +1,52 @@
# https://www.waveshare.com/pico-lora-sx1262-868m.htm
# http://www.orangepi.org/html/hardWare/computerAndMicrocontrollers/details/Orange-Pi-Zero-3.html
#
# See Orange Pi Zero3 manual, chapter 3.16, page 124 for 26-pin header pinout
#
# Pin Connection
# Waveshare Orange Pi Zero3
# 36 3.3V 17
# 15 MOSI 19
# 16 MISO 21
# 14 CLK 23
# 38 GND 25
# 4 BUSY 18
# 20 RESET 22
# 5 CS 24
# 26 DIO1/IRQ 26
Lora:
Module: sx1262 # Waveshare Raspberry Pico Lora module
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true
# Specify either the spidev1_1 or the CS below, not both!
# On DietPi Linux, when using the user overlay dietpi-spi1_1.dtbo, CS will be configured with spidev1.1
spidev: spidev1.1 # See Orange Pi Zero3 manual, chapter 3.18.3, page 130
# CS: # CS PIN_24 -> chip 1, line 233
# pin: 24
# gpiochip: 1
# line: 233
SCK: # SCK PIN_23 -> chip 1, line 230
pin: 23
gpiochip: 1
line: 230
Busy: # BUSY PIN_18 -> chip 1, line 78
pin: 18
gpiochip: 1
line: 78
MOSI: # MOSI PIN_19 -> chip 1, line 231
pin: 19
gpiochip: 1
line: 231
MISO: # MISO PIN_21 -> chip 1, line 232
pin: 21
gpiochip: 1
line: 232
Reset: # NRST PIN_22 -> chip 1, line 71
pin: 22
gpiochip: 1
line: 71
IRQ: # DIO1 PIN_26 -> chip 1, line 74
pin: 26
gpiochip: 1
line: 74

View File

@@ -31,17 +31,16 @@ EOF
}
# Check for --change-mode and remove it from arguments
NEW_ARGS=""
NEW_ARGS=()
for arg in "$@"; do
if [ "$arg" = "--change-mode" ]; then
CHANGE_MODE=true
else
NEW_ARGS="$NEW_ARGS \"\$arg\""
NEW_ARGS+=("$arg")
fi
done
# Reset positional parameters to filtered list
eval set -- $NEW_ARGS
set -- "${NEW_ARGS[@]}"
while getopts ":hp:P:f:" opt; do
case "${opt}" in

View File

@@ -87,6 +87,9 @@
</screenshots>
<releases>
<release version="2.7.3" date="2025-07-10">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.3</url>
</release>
<release version="2.7.2" date="2025-07-04">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.2</url>
</release>

View File

@@ -3,6 +3,7 @@
# trunk-ignore-all(flake8/F821): For SConstruct imports
import sys
from os.path import join
import subprocess
import json
import re
@@ -92,6 +93,17 @@ prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
verObj = readProps(prefsLoc)
print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"] + " on " + env.get("PIOENV"))
# get repository owner if git is installed
try:
r_owner = (
subprocess.check_output(["git", "config", "--get", "remote.origin.url"])
.decode("utf-8")
.strip().split("/")
)
repo_owner = r_owner[-2] + "/" + r_owner[-1].replace(".git", "")
except subprocess.CalledProcessError:
repo_owner = "unknown"
jsonLoc = env["PROJECT_DIR"] + "/userPrefs.jsonc"
with open(jsonLoc) as f:
jsonStr = re.sub("//.*","", f.read(), flags=re.MULTILINE)
@@ -117,6 +129,7 @@ flags = [
"-DAPP_VERSION=" + verObj["long"],
"-DAPP_VERSION_SHORT=" + verObj["short"],
"-DAPP_ENV=" + env.get("PIOENV"),
"-DAPP_REPO=" + repo_owner,
] + pref_flags
print ("Using flags:")

7
debian/changelog vendored
View File

@@ -1,4 +1,4 @@
meshtasticd (2.7.2.0) UNRELEASED; urgency=medium
meshtasticd (2.7.3.0) UNRELEASED; urgency=medium
[ Austin Lane ]
* Initial packaging
@@ -28,4 +28,7 @@ meshtasticd (2.7.2.0) UNRELEASED; urgency=medium
[ ]
* GitHub Actions Automatic version bump
-- <github-actions[bot]@users.noreply.github.com> Fri, 04 Jul 2025 11:58:01 +0000
[ Ubuntu ]
* GitHub Actions Automatic version bump
-- Ubuntu <github-actions[bot]@users.noreply.github.com> Thu, 10 Jul 2025 16:29:27 +0000

View File

@@ -104,18 +104,18 @@ lib_deps =
[radiolib_base]
lib_deps =
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
jgromes/RadioLib@7.2.0
jgromes/RadioLib@7.2.1
[device-ui_base]
lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/8c7092c73425adfda1aac8c6960df06cd85f6d92.zip
https://github.com/meshtastic/device-ui/archive/86a09a7360f92d10053fbbf8d74f67f85b0ceb09.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]
lib_deps =
# renovate: datasource=custom.pio depName=Adafruit BusIO packageName=adafruit/library/Adafruit BusIO
adafruit/Adafruit BusIO@1.17.1
adafruit/Adafruit BusIO@1.17.2
# renovate: datasource=custom.pio depName=Adafruit Unified Sensor packageName=adafruit/library/Adafruit Unified Sensor
adafruit/Adafruit Unified Sensor@1.1.15
# renovate: datasource=custom.pio depName=Adafruit BMP280 packageName=adafruit/library/Adafruit BMP280 Library

View File

@@ -39,9 +39,9 @@ template <typename T, std::size_t N> std::size_t array_count(const T (&)[N])
return N;
}
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
#if defined(RAK2560)
HardwareSerial *GPS::_serial_gps = &Serial2;
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
#if defined(GPS_SERIAL_PORT)
HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT;
#else
HardwareSerial *GPS::_serial_gps = &Serial1;
#endif

View File

@@ -226,7 +226,14 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
time_t res = gm_mktime(&t);
struct timeval tv;
tv.tv_sec = res;
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
tv.tv_usec = 0; // time.centisecond() * (10 / 1000);
uint32_t printableEpoch = tv.tv_sec; // Print lib only supports 32 bit but time_t can be 64 bit on some platforms
#ifdef BUILD_EPOCH
if (tv.tv_sec < BUILD_EPOCH) {
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
return RTCSetResultInvalidTime;
}
#endif
// LOG_DEBUG("Got time from GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec);
if (t.tm_year < 0 || t.tm_year >= 300) {

View File

@@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Screen.h"
#include "NodeDB.h"
#include "PowerMon.h"
#include "Throttle.h"
#include "configuration.h"
@@ -44,7 +45,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#endif
#include "FSCommon.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RadioLibInterface.h"
#include "error.h"
#include "gps/GeoCoord.h"
@@ -386,9 +386,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
#ifdef T_WATCH_S3
PMU->enablePowerOutput(XPOWERS_ALDO2);
#endif
#ifdef HELTEC_TRACKER_V1_X
uint8_t tft_vext_enabled = digitalRead(VEXT_ENABLE);
#endif
#if !ARCH_PORTDUINO
dispdev->displayOn();
#endif
@@ -400,10 +398,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
dispdev->displayOn();
#ifdef HELTEC_TRACKER_V1_X
// If the TFT VEXT power is not enabled, initialize the UI.
if (!tft_vext_enabled) {
ui->init();
}
ui->init();
#endif
#ifdef USE_ST7789
pinMode(VTFT_CTRL, OUTPUT);
@@ -640,6 +635,11 @@ void Screen::forceDisplay(bool forceUiUpdate)
// Tell EInk class to update the display
static_cast<EInkDisplay *>(dispdev)->forceDisplay();
#else
// No delay between UI frame rendering
if (forceUiUpdate) {
setFastFramerate();
}
#endif
}
@@ -864,6 +864,8 @@ void Screen::setFrames(FrameFocus focus)
uint8_t previousFrameCount = framesetInfo.frameCount;
FramesetInfo fsi; // Location of specific frames, for applying focus parameter
graphics::UIRenderer::rebuildFavoritedNodes();
LOG_DEBUG("Show standard frames");
showingNormalScreen = true;
@@ -1257,40 +1259,45 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
devicestate.has_rx_text_message = true; // Needed to include the message frame
hasUnreadMessage = true; // Enables mail icon in the header
setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view
forceDisplay(); // Forces screen redraw
// === Prepare banner content ===
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
// Only wake/force display if the configuration allows it
if (shouldWakeOnReceivedMessage()) {
setOn(true); // Wake up the screen first
forceDisplay(); // Forces screen redraw
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
// === Prepare banner content ===
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
char banner[256];
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
// Check for bell character in message to determine alert type
bool isAlert = false;
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
if (msgRaw[i] == '\x07') {
isAlert = true;
break;
char banner[256];
// Check for bell character in message to determine alert type
bool isAlert = false;
for (size_t i = 0; i < packet->decoded.payload.size && i < 100; i++) {
if (msgRaw[i] == '\x07') {
isAlert = true;
break;
}
}
}
if (isAlert) {
if (longName && longName[0]) {
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
if (isAlert) {
if (longName && longName[0]) {
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
} else {
strcpy(banner, "Alert Received");
}
} else {
strcpy(banner, "Alert Received");
if (longName && longName[0]) {
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
} else {
strcpy(banner, "New Message");
}
}
} else {
if (longName && longName[0]) {
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
} else {
strcpy(banner, "New Message");
}
}
screen->showSimpleBanner(banner, 3000);
screen->showSimpleBanner(banner, 3000);
}
}
}
@@ -1369,9 +1376,12 @@ int Screen::handleInputEvent(const InputEvent *event)
menuHandler::clockMenu();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) {
menuHandler::LoraRegionPicker();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage &&
devicestate.rx_text_message.from) {
menuHandler::messageResponseMenu();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) {
if (devicestate.rx_text_message.from) {
menuHandler::messageResponseMenu();
} else {
menuHandler::textMessageBaseMenu();
}
} else if (framesetInfo.positions.firstFavorite != 255 &&
this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite &&
this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) {
@@ -1423,3 +1433,23 @@ bool Screen::isOverlayBannerShowing()
#else
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
#endif // HAS_SCREEN
bool shouldWakeOnReceivedMessage()
{
/*
The goal here is to determine when we do NOT wake up the screen on message received:
- Any ext. notifications are turned on
- If role is not client / client_mute
- If the battery level is very low
*/
if (moduleConfig.external_notification.enabled) {
return false;
}
if (!meshtastic_Config_DeviceConfig_Role_CLIENT && !meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE) {
return false;
}
if (powerStatus && powerStatus->getBatteryChargePercent() < 10) {
return false;
}
return true;
}

View File

@@ -26,6 +26,8 @@ struct BannerOverlayOptions {
};
} // namespace graphics
bool shouldWakeOnReceivedMessage();
#if !HAS_SCREEN
#include "power.h"
namespace graphics

View File

@@ -206,7 +206,7 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *ti
timeX = screenW - xOffset - timeStrWidth + 3;
// === Show Mail or Mute Icon to the Left of Time ===
int iconRightEdge = timeX - 1;
int iconRightEdge = timeX - 2;
bool showMail = false;

View File

@@ -186,7 +186,7 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
{
display->clear();
display->setTextAlignment(TEXT_ALIGN_LEFT);
int line = 1;
// === Set Title, Blank for Clock
const char *titleStr = "";
// === Header ===
@@ -230,6 +230,8 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
#ifdef T_WATCH_S3
float scale = 1.5;
#elif defined(CHATTER_2)
float scale = 1.1;
#else
float scale = 0.75;
if (isHighResolution) {
@@ -285,6 +287,9 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
int yOffset = (isHighResolution) ? 3 : 1;
#ifdef SENSECAP_INDICATOR
yOffset -= 3;
#endif
#ifdef T_DECK
yOffset -= 5;
#endif
if (config.display.use_12h_clock) {
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,

View File

@@ -483,7 +483,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
}
// ****************************
// * Memory Screen *
// * System Screen *
// ****************************
void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -593,7 +593,19 @@ void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
}
line += 1;
char appversionstr[35];
snprintf(appversionstr, sizeof(appversionstr), "Ver.: %s", optstr(APP_VERSION));
snprintf(appversionstr, sizeof(appversionstr), "Ver: %s", optstr(APP_VERSION));
char appversionstr_formatted[40];
char *lastDot = strrchr(appversionstr, '.');
if (lastDot) {
size_t prefixLen = lastDot - appversionstr;
strncpy(appversionstr_formatted, appversionstr, prefixLen);
appversionstr_formatted[prefixLen] = '\0';
strncat(appversionstr_formatted, " (", sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1);
strncat(appversionstr_formatted, lastDot + 1, sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1);
strncat(appversionstr_formatted, ")", sizeof(appversionstr_formatted) - strlen(appversionstr_formatted) - 1);
strncpy(appversionstr, appversionstr_formatted, sizeof(appversionstr) - 1);
appversionstr[sizeof(appversionstr) - 1] = '\0';
}
int textWidth = display->getStringWidth(appversionstr);
int nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line], appversionstr);

View File

@@ -129,11 +129,11 @@ void menuHandler::ClockFacePicker()
screen->runNow();
} else if (selected == Digital) {
uiconfig.is_clockface_analog = false;
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
saveUIConfig();
screen->setFrames(Screen::FOCUS_CLOCK);
} else {
uiconfig.is_clockface_analog = true;
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
saveUIConfig();
screen->setFrames(Screen::FOCUS_CLOCK);
}
};
@@ -344,39 +344,53 @@ void menuHandler::homeBaseMenu()
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::textMessageBaseMenu()
{
enum optionsNumbers { Back, Preset, Freetext, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
optionsArray[options] = "New Preset Msg";
optionsEnumArray[options++] = Preset;
if (kb_found) {
optionsArray[options] = "New Freetext Msg";
optionsEnumArray[options++] = Freetext;
}
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Message Action";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Preset) {
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
} else if (selected == Freetext) {
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST);
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::systemBaseMenu()
{
// Check if brightness is supported
bool hasSupportBrightness = false;
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || HAS_TFT
hasSupportBrightness = true;
#endif
enum optionsNumbers { Back, Beeps, Brightness, Reboot, Color, MUI, Test, enumEnd };
enum optionsNumbers { Back, Notifications, ScreenOptions, PowerMenu, Test, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
optionsArray[options] = "Reboot";
optionsEnumArray[options++] = Reboot;
optionsArray[options] = "Beeps Action";
optionsEnumArray[options++] = Beeps;
if (hasSupportBrightness) {
optionsArray[options] = "Brightness";
optionsEnumArray[options++] = Brightness;
}
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
optionsArray[options] = "Screen Color";
optionsEnumArray[options++] = Color;
#endif
#if HAS_TFT
optionsArray[options] = "Switch to MUI";
optionsEnumArray[options++] = MUI;
optionsArray[options] = "Notifications";
optionsEnumArray[options++] = Notifications;
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || \
defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
optionsArray[options] = "Screen Options";
optionsEnumArray[options++] = ScreenOptions;
#endif
optionsArray[options] = "Reboot/Shutdown";
optionsEnumArray[options++] = PowerMenu;
if (test_enabled) {
optionsArray[options] = "Test Menu";
optionsEnumArray[options++] = Test;
@@ -388,20 +402,14 @@ void menuHandler::systemBaseMenu()
bannerOptions.optionsCount = options;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Beeps) {
menuHandler::menuQueue = menuHandler::buzzermodemenupicker;
if (selected == Notifications) {
menuHandler::menuQueue = menuHandler::notifications_menu;
screen->runNow();
} else if (selected == Brightness) {
menuHandler::menuQueue = menuHandler::brightness_picker;
} else if (selected == ScreenOptions) {
menuHandler::menuQueue = menuHandler::screen_options_menu;
screen->runNow();
} else if (selected == Reboot) {
menuHandler::menuQueue = menuHandler::reboot_menu;
screen->runNow();
} else if (selected == MUI) {
menuHandler::menuQueue = menuHandler::mui_picker;
screen->runNow();
} else if (selected == Color) {
menuHandler::menuQueue = menuHandler::tftcolormenupicker;
} else if (selected == PowerMenu) {
menuHandler::menuQueue = menuHandler::power_menu;
screen->runNow();
} else if (selected == Test) {
menuHandler::menuQueue = menuHandler::test_menu;
@@ -436,11 +444,11 @@ void menuHandler::favoriteBaseMenu()
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.optionsCount = options;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) {
if (selected == Preset) {
cannedMessageModule->LaunchWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
} else if (selected == 2 && kb_found) {
} else if (selected == Freetext) {
cannedMessageModule->LaunchFreetextWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
} else if ((!kb_found && selected == 2) || (selected == 3 && kb_found)) {
} else if (selected == Remove) {
menuHandler::menuQueue = menuHandler::remove_favorite;
screen->runNow();
}
@@ -533,22 +541,19 @@ void menuHandler::compassNorthMenu()
if (selected == Dynamic) {
if (uiconfig.compass_mode != meshtastic_CompassMode_DYNAMIC) {
uiconfig.compass_mode = meshtastic_CompassMode_DYNAMIC;
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
&uiconfig);
saveUIConfig();
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
}
} else if (selected == Fixed) {
if (uiconfig.compass_mode != meshtastic_CompassMode_FIXED_RING) {
uiconfig.compass_mode = meshtastic_CompassMode_FIXED_RING;
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
&uiconfig);
saveUIConfig();
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
}
} else if (selected == Freeze) {
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
uiconfig.compass_mode = meshtastic_CompassMode_FREEZE_HEADING;
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg,
&uiconfig);
saveUIConfig();
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
}
} else if (selected == Back) {
@@ -610,7 +615,7 @@ void menuHandler::BuzzerModeMenu()
{
static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Beep Action";
bannerOptions.message = "Buzzer Mode";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 4;
bannerOptions.bannerCallback = [](int selected) -> void {
@@ -660,7 +665,7 @@ void menuHandler::BrightnessPickerMenu()
#endif
// Save to device
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
saveUIConfig();
LOG_INFO("Screen brightness set to %d", uiconfig.screen_brightness);
}
@@ -671,13 +676,13 @@ void menuHandler::BrightnessPickerMenu()
void menuHandler::switchToMUIMenu()
{
static const char *optionsArray[] = {"Yes", "No"};
static const char *optionsArray[] = {"No", "Yes"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Switch to MUI?";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 2;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 0) {
if (selected == 1) {
config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR;
config.bluetooth.enabled = false;
service->reloadConfig(SEGMENT_CONFIG);
@@ -696,6 +701,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 10;
bannerOptions.bannerCallback = [display](int selected) -> void {
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT
uint8_t TFT_MESH_r = 0;
uint8_t TFT_MESH_g = 0;
uint8_t TFT_MESH_b = 0;
@@ -742,9 +748,11 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
TFT_MESH_r = 255;
TFT_MESH_g = 255;
TFT_MESH_b = 255;
} else {
menuQueue = system_base_menu;
screen->runNow();
}
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
if (selected != 0) {
display->setColor(BLACK);
display->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
@@ -771,7 +779,7 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
uiconfig.screen_rgb_color = (TFT_MESH_r << 16) | (TFT_MESH_g << 8) | TFT_MESH_b;
}
LOG_INFO("Storing Value of %d to uiconfig.screen_rgb_color", uiconfig.screen_rgb_color);
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
saveUIConfig();
}
#endif
};
@@ -790,6 +798,29 @@ void menuHandler::rebootMenu()
IF_SCREEN(screen->showSimpleBanner("Rebooting...", 0));
nodeDB->saveToDisk();
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
} else {
menuQueue = power_menu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::shutdownMenu()
{
static const char *optionsArray[] = {"Back", "Confirm"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Shutdown Device?";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 2;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) {
IF_SCREEN(screen->showSimpleBanner("Shutting Down...", 0));
nodeDB->saveToDisk();
power->shutdown();
} else {
menuQueue = power_menu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
@@ -819,8 +850,9 @@ void menuHandler::removeFavoriteMenu()
bannerOptions.optionsCount = 2;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) {
LOG_INFO("Removing %x as favorite node", graphics::UIRenderer::currentFavoriteNodeNum);
nodeDB->set_favorite(false, graphics::UIRenderer::currentFavoriteNodeNum);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
screen->setFrames(graphics::Screen::FOCUS_DEFAULT);
}
};
screen->showOverlayBanner(bannerOptions);
@@ -888,6 +920,122 @@ void menuHandler::wifiToggleMenu()
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::notificationsMenu()
{
enum optionsNumbers { Back, BuzzerActions };
static const char *optionsArray[] = {"Back", "Buzzer Actions"};
static int optionsEnumArray[] = {Back, BuzzerActions};
int options = 2;
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Notifications";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = options;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == BuzzerActions) {
menuHandler::menuQueue = menuHandler::buzzermodemenupicker;
screen->runNow();
} else {
menuQueue = system_base_menu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::screenOptionsMenu()
{
// Check if brightness is supported
bool hasSupportBrightness = false;
#if defined(ST7789_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
hasSupportBrightness = true;
#endif
#if defined(T_DECK)
// TDeck Doesn't seem to support brightness at all, at least not reliably
hasSupportBrightness = false;
#endif
enum optionsNumbers { Back, Brightness, ScreenColor };
static const char *optionsArray[4] = {"Back"};
static int optionsEnumArray[4] = {Back};
int options = 1;
// Only show brightness for B&W displays
if (hasSupportBrightness) {
optionsArray[options] = "Brightness";
optionsEnumArray[options++] = Brightness;
}
// Only show screen color for TFT displays
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || HAS_TFT
optionsArray[options] = "Screen Color";
optionsEnumArray[options++] = ScreenColor;
#endif
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Screen Options";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = options;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Brightness) {
menuHandler::menuQueue = menuHandler::brightness_picker;
screen->runNow();
} else if (selected == ScreenColor) {
menuHandler::menuQueue = menuHandler::tftcolormenupicker;
screen->runNow();
} else {
menuQueue = system_base_menu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::powerMenu()
{
enum optionsNumbers { Back, Reboot, Shutdown, MUI };
static const char *optionsArray[4] = {"Back"};
static int optionsEnumArray[4] = {Back};
int options = 1;
optionsArray[options] = "Reboot";
optionsEnumArray[options++] = Reboot;
optionsArray[options] = "Shutdown";
optionsEnumArray[options++] = Shutdown;
#if HAS_TFT
optionsArray[options] = "Switch to MUI";
optionsEnumArray[options++] = MUI;
#endif
BannerOverlayOptions bannerOptions;
bannerOptions.message = "Reboot / Shutdown";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = options;
bannerOptions.optionsEnumPtr = optionsEnumArray;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Reboot) {
menuHandler::menuQueue = menuHandler::reboot_menu;
screen->runNow();
} else if (selected == Shutdown) {
menuHandler::menuQueue = menuHandler::shutdown_menu;
screen->runNow();
} else if (selected == MUI) {
menuHandler::menuQueue = menuHandler::mui_picker;
screen->runNow();
} else {
menuQueue = system_base_menu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::keyVerificationInitMenu()
{
screen->showNodePicker("Node to Verify", 30000,
@@ -941,6 +1089,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case clock_menu:
clockMenu();
break;
case system_base_menu:
systemBaseMenu();
break;
case position_base_menu:
positionBaseMenu();
break;
@@ -970,6 +1121,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case reboot_menu:
rebootMenu();
break;
case shutdown_menu:
shutdownMenu();
break;
case add_favorite:
addFavoriteMenu();
break;
@@ -994,6 +1148,15 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case bluetooth_toggle_menu:
BluetoothToggleMenu();
break;
case notifications_menu:
notificationsMenu();
break;
case screen_options_menu:
screenOptionsMenu();
break;
case power_menu:
powerMenu();
break;
case throttle_message:
screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
break;
@@ -1001,6 +1164,11 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
menuQueue = menu_none;
}
void menuHandler::saveUIConfig()
{
nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig);
}
} // namespace graphics
#endif

View File

@@ -23,14 +23,19 @@ class menuHandler
tftcolormenupicker,
brightness_picker,
reboot_menu,
shutdown_menu,
add_favorite,
remove_favorite,
test_menu,
number_test,
wifi_toggle_menu,
bluetooth_toggle_menu,
notifications_menu,
screen_options_menu,
power_menu,
system_base_menu,
key_verification_init,
key_verification_final_prompt,
bluetooth_toggle_menu,
throttle_message
};
static screenMenus menuQueue;
@@ -43,6 +48,7 @@ class menuHandler
static void ClockFacePicker();
static void messageResponseMenu();
static void homeBaseMenu();
static void textMessageBaseMenu();
static void systemBaseMenu();
static void favoriteBaseMenu();
static void positionBaseMenu();
@@ -55,12 +61,19 @@ class menuHandler
static void resetNodeDBMenu();
static void BrightnessPickerMenu();
static void rebootMenu();
static void shutdownMenu();
static void addFavoriteMenu();
static void removeFavoriteMenu();
static void testMenu();
static void numberTest();
static void wifiBaseMenu();
static void wifiToggleMenu();
static void notificationsMenu();
static void screenOptionsMenu();
static void powerMenu();
private:
static void saveUIConfig();
static void keyVerificationInitMenu();
static void keyVerificationFinalPrompt();
static void BluetoothToggleMenu();

View File

@@ -156,7 +156,7 @@ void NotificationRenderer::drawNumberPicker(OLEDDisplay *display, OLEDDisplayUiS
resetBanner();
return;
}
if (curSelected == numDigits) {
if (curSelected == static_cast<int8_t>(numDigits)) {
alertBannerCallback(currentNumber);
resetBanner();
return;

View File

@@ -24,6 +24,23 @@ extern graphics::Screen *screen;
namespace graphics
{
NodeNum UIRenderer::currentFavoriteNodeNum = 0;
std::vector<meshtastic_NodeInfoLite *> graphics::UIRenderer::favoritedNodes;
void graphics::UIRenderer::rebuildFavoritedNodes()
{
favoritedNodes.clear();
size_t total = nodeDB->getNumMeshNodes();
for (size_t i = 0; i < total; i++) {
meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
if (!n || n->num == nodeDB->getNodeNum())
continue;
if (n->is_favorite)
favoritedNodes.push_back(n);
}
std::sort(favoritedNodes.begin(), favoritedNodes.end(),
[](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; });
}
#if !MESHTASTIC_EXCLUDE_GPS
// GeoCoord object for coordinate conversions
@@ -201,27 +218,7 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
// **********************
void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// --- Cache favorite nodes for the current frame only, to save computation ---
static std::vector<meshtastic_NodeInfoLite *> favoritedNodes;
static int prevFrame = -1;
// --- Only rebuild favorites list if we're on a new frame ---
if (state->currentFrame != prevFrame) {
prevFrame = state->currentFrame;
favoritedNodes.clear();
size_t total = nodeDB->getNumMeshNodes();
for (size_t i = 0; i < total; i++) {
meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
// Skip nulls and ourself
if (!n || n->num == nodeDB->getNodeNum())
continue;
if (n->is_favorite)
favoritedNodes.push_back(n);
}
// Keep a stable, consistent display order
std::sort(favoritedNodes.begin(), favoritedNodes.end(),
[](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; });
}
if (favoritedNodes.empty())
return;
@@ -657,7 +654,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
char combinedName[50];
snprintf(combinedName, sizeof(combinedName), "%s (%s)", longNameStr.empty() ? "" : longNameStr.c_str(), shortnameble);
if (SCREEN_WIDTH - (display->getStringWidth(longName) + display->getStringWidth(shortnameble)) > 10) {
if (SCREEN_WIDTH - (display->getStringWidth(combinedName)) > 10) {
size_t len = strlen(combinedName);
if (len >= 3 && strcmp(combinedName + len - 3, " ()") == 0) {
combinedName[len - 3] = '\0'; // Remove the last three characters
@@ -668,7 +665,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
nameX, ((rows == 4) ? getTextPositions(display)[line++] : getTextPositions(display)[line++]) + yOffset, combinedName);
} else {
// === LongName Centered ===
textWidth = display->getStringWidth(longName);
textWidth = display->getStringWidth(longNameStr.c_str());
nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line++], longNameStr.c_str());

View File

@@ -61,6 +61,8 @@ class UIRenderer
static void drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
static NodeNum currentFavoriteNodeNum;
static std::vector<meshtastic_NodeInfoLite *> favoritedNodes;
static void rebuildFavoritedNodes();
// OEM screens
#ifdef USERPREFS_OEM_TEXT

View File

@@ -39,8 +39,8 @@ void InkHUD::Events::begin()
void InkHUD::Events::onButtonShort()
{
// Audio feedback (via buzzer)
// Short low tone
playBoop();
// Short tone
playChirp();
// Cancel any beeping, buzzing, blinking
// Some button handling suppressed if we are dismissing an external notification (see below)
bool dismissedExt = dismissExternalNotification();
@@ -64,8 +64,8 @@ void InkHUD::Events::onButtonShort()
void InkHUD::Events::onButtonLong()
{
// Audio feedback (via buzzer)
// Low tone, longer than playBoop
playBeep();
// Slightly longer than playChirp
playBoop();
// Check which system applet wants to handle the button press (if any)
SystemApplet *consumer = nullptr;

View File

@@ -0,0 +1,83 @@
#ifdef ARCH_PORTDUINO
#include "SeesawRotary.h"
#include "input/InputBroker.h"
using namespace concurrency;
SeesawRotary *seesawRotary;
SeesawRotary::SeesawRotary(const char *name) : OSThread(name)
{
_originName = name;
}
bool SeesawRotary::init()
{
if (inputBroker)
inputBroker->registerSource(this);
if (!ss.begin(SEESAW_ADDR)) {
return false;
}
// attachButtonInterrupts();
uint32_t version = ((ss.getVersion() >> 16) & 0xFFFF);
if (version != 4991) {
LOG_WARN("Wrong firmware loaded? %u", version);
} else {
LOG_INFO("Found Product 4991");
}
/*
#ifdef ARCH_ESP32
// Register callbacks for before and after lightsleep
// Used to detach and reattach interrupts
lsObserver.observe(&notifyLightSleep);
lsEndObserver.observe(&notifyLightSleepEnd);
#endif
*/
ss.pinMode(SS_SWITCH, INPUT_PULLUP);
// get starting position
encoder_position = ss.getEncoderPosition();
ss.setGPIOInterrupts((uint32_t)1 << SS_SWITCH, 1);
ss.enableEncoderInterrupt();
canSleep = true; // Assume we should not keep the board awake
return true;
}
int32_t SeesawRotary::runOnce()
{
InputEvent e;
e.inputEvent = INPUT_BROKER_NONE;
bool currentlyPressed = !ss.digitalRead(SS_SWITCH);
if (currentlyPressed && !wasPressed) {
e.inputEvent = INPUT_BROKER_SELECT;
}
wasPressed = currentlyPressed;
int32_t new_position = ss.getEncoderPosition();
// did we move arounde?
if (encoder_position != new_position) {
if (encoder_position == 0 && new_position != 1) {
e.inputEvent = INPUT_BROKER_ALT_PRESS;
} else if (new_position == 0 && encoder_position != 1) {
e.inputEvent = INPUT_BROKER_USER_PRESS;
} else if (new_position > encoder_position) {
e.inputEvent = INPUT_BROKER_USER_PRESS;
} else {
e.inputEvent = INPUT_BROKER_ALT_PRESS;
}
encoder_position = new_position;
}
if (e.inputEvent != INPUT_BROKER_NONE) {
e.source = this->_originName;
e.kbchar = 0x00;
this->notifyObservers(&e);
}
return 50;
}
#endif

29
src/input/SeesawRotary.h Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#ifdef ARCH_PORTDUINO
#include "Adafruit_seesaw.h"
#include "InputBroker.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
#define SS_SWITCH 24
#define SS_NEOPIX 6
#define SEESAW_ADDR 0x36
class SeesawRotary : public Observable<const InputEvent *>, public concurrency::OSThread
{
public:
const char *_originName;
bool init();
SeesawRotary(const char *name);
int32_t runOnce() override;
private:
Adafruit_seesaw ss;
int32_t encoder_position;
bool wasPressed = false;
};
extern SeesawRotary *seesawRotary;
#endif

View File

@@ -67,4 +67,5 @@ void CardKbI2cImpl::init()
}
#endif
inputBroker->registerSource(this);
kb_found = true;
}

View File

@@ -286,7 +286,7 @@ void lateInitVariant() {}
*/
void printInfo()
{
LOG_INFO("S:B:%d,%s", HW_VENDOR, optstr(APP_VERSION));
LOG_INFO("S:B:%d,%s,%s,%s", HW_VENDOR, optstr(APP_VERSION), optstr(APP_ENV), optstr(APP_REPO));
}
#ifndef PIO_UNIT_TESTING
void setup()
@@ -515,25 +515,11 @@ void setup()
LOG_INFO("Scan for i2c devices");
#endif
#if defined(I2C_SDA1) && defined(ARCH_RP2040)
Wire1.setSDA(I2C_SDA1);
Wire1.setSCL(I2C_SCL1);
Wire1.begin();
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1);
#elif defined(I2C_SDA1) && !defined(ARCH_RP2040)
Wire1.begin(I2C_SDA1, I2C_SCL1);
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1);
#elif defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2)
#if defined(I2C_SDA1) || (defined(NRF52840_XXAA) && (WIRE_INTERFACES_COUNT == 2))
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE1);
#endif
#if defined(I2C_SDA) && defined(ARCH_RP2040)
Wire.setSDA(I2C_SDA);
Wire.setSCL(I2C_SCL);
Wire.begin();
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE);
#elif defined(I2C_SDA) && !defined(ARCH_RP2040)
Wire.begin(I2C_SDA, I2C_SCL);
#if defined(I2C_SDA)
i2cScanner->scanPort(ScanI2C::I2CPort::WIRE);
#elif defined(ARCH_PORTDUINO)
if (settingsStrings[i2cdev] != "") {

View File

@@ -10,6 +10,10 @@
#include "memGet.h"
#include "configuration.h"
#ifdef ARCH_STM32WL
#include <malloc.h>
#endif
MemGet memGet;
/**
@@ -24,6 +28,9 @@ uint32_t MemGet::getFreeHeap()
return dbgHeapFree();
#elif defined(ARCH_RP2040)
return rp2040.getFreeHeap();
#elif defined(ARCH_STM32WL)
struct mallinfo m = mallinfo();
return m.fordblks; // Total free space (bytes)
#else
// this platform does not have heap management function implemented
return UINT32_MAX;
@@ -42,6 +49,9 @@ uint32_t MemGet::getHeapSize()
return dbgHeapTotal();
#elif defined(ARCH_RP2040)
return rp2040.getTotalHeap();
#elif defined(ARCH_STM32WL)
struct mallinfo m = mallinfo();
return m.arena; // Non-mmapped space allocated (bytes)
#else
// this platform does not have heap management function implemented
return UINT32_MAX;

View File

@@ -24,6 +24,11 @@
#define min_node_info_broadcast_secs 60 * 60 // No regular broadcasts of more than once an hour
#define min_neighbor_info_broadcast_secs 4 * 60 * 60
#define default_map_publish_interval_secs 60 * 60
#ifdef USERPREFS_RINGTONE_NAG_SECS
#define default_ringtone_nag_secs USERPREFS_RINGTONE_NAG_SECS
#else
#define default_ringtone_nag_secs 60
#endif
#define default_mqtt_address "mqtt.meshtastic.org"
#define default_mqtt_username "meshdev"

View File

@@ -344,6 +344,22 @@ NodeDB::NodeDB()
config.device.node_info_broadcast_secs = MAX_INTERVAL;
if (config.position.position_broadcast_secs > MAX_INTERVAL)
config.position.position_broadcast_secs = MAX_INTERVAL;
if (config.position.gps_update_interval > MAX_INTERVAL)
config.position.gps_update_interval = MAX_INTERVAL;
if (config.position.gps_attempt_time > MAX_INTERVAL)
config.position.gps_attempt_time = MAX_INTERVAL;
if (config.position.position_flags > MAX_INTERVAL)
config.position.position_flags = MAX_INTERVAL;
if (config.position.rx_gpio > MAX_INTERVAL)
config.position.rx_gpio = MAX_INTERVAL;
if (config.position.tx_gpio > MAX_INTERVAL)
config.position.tx_gpio = MAX_INTERVAL;
if (config.position.broadcast_smart_minimum_distance > MAX_INTERVAL)
config.position.broadcast_smart_minimum_distance = MAX_INTERVAL;
if (config.position.broadcast_smart_minimum_interval_secs > MAX_INTERVAL)
config.position.broadcast_smart_minimum_interval_secs = MAX_INTERVAL;
if (config.position.gps_en_gpio > MAX_INTERVAL)
config.position.gps_en_gpio = MAX_INTERVAL;
if (moduleConfig.neighbor_info.update_interval > MAX_INTERVAL)
moduleConfig.neighbor_info.update_interval = MAX_INTERVAL;
if (moduleConfig.telemetry.device_update_interval > MAX_INTERVAL)
@@ -778,7 +794,7 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.output_buzzer = PIN_BUZZER;
moduleConfig.external_notification.use_pwm = true;
moduleConfig.external_notification.alert_message_buzzer = true;
moduleConfig.external_notification.nag_timeout = 60;
moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;
#endif
#if defined(RAK4630) || defined(RAK11310) || defined(RAK3312)
// Default to RAK led pin 2 (blue)
@@ -787,7 +803,7 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.active = true;
moduleConfig.external_notification.alert_message = true;
moduleConfig.external_notification.output_ms = 1000;
moduleConfig.external_notification.nag_timeout = 60;
moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;
#endif
#ifdef HAS_I2S
@@ -796,10 +812,10 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.use_i2s_as_buzzer = true;
moduleConfig.external_notification.alert_message_buzzer = true;
#if HAS_TFT
if (moduleConfig.external_notification.nag_timeout == 60)
if (moduleConfig.external_notification.nag_timeout == default_ringtone_nag_secs)
moduleConfig.external_notification.nag_timeout = 0;
#else
moduleConfig.external_notification.nag_timeout = 60;
moduleConfig.external_notification.nag_timeout = default_ringtone_nag_secs;
#endif
#endif
#ifdef NANO_G2_ULTRA
@@ -1309,6 +1325,13 @@ void NodeDB::loadFromDisk()
saveToDisk(SEGMENT_MODULECONFIG);
}
#if ARCH_PORTDUINO
// set any config overrides
if (settingsMap[has_configDisplayMode]) {
config.display.displaymode = (_meshtastic_Config_DisplayConfig_DisplayMode)settingsMap[configDisplayMode];
}
#endif
}
/** Save a protobuf from a file, return true for success */

View File

@@ -7,9 +7,9 @@
#include "meshtastic/channel.pb.h"
#include "meshtastic/config.pb.h"
#include "meshtastic/connection_status.pb.h"
#include "meshtastic/device_ui.pb.h"
#include "meshtastic/mesh.pb.h"
#include "meshtastic/module_config.pb.h"
#include "meshtastic/device_ui.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.

View File

@@ -102,7 +102,11 @@ typedef enum _meshtastic_Config_DeviceConfig_BuzzerMode {
meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY = 2,
/* Non-notification system buzzer tones only.
Buzzer is enabled only for non-notification tones such as button presses, startup, shutdown, but not for alerts. */
meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY = 3
meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY = 3,
/* Direct Message notifications only.
Buzzer is enabled only for direct messages and alerts, but not for button presses.
External notification config determines the specifics of the notification behavior. */
meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY = 4
} meshtastic_Config_DeviceConfig_BuzzerMode;
/* Bit field of boolean configuration options, indicating which optional
@@ -645,8 +649,8 @@ extern "C" {
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_RebroadcastMode)(meshtastic_Config_DeviceConfig_RebroadcastMode_CORE_PORTNUMS_ONLY+1))
#define _meshtastic_Config_DeviceConfig_BuzzerMode_MIN meshtastic_Config_DeviceConfig_BuzzerMode_ALL_ENABLED
#define _meshtastic_Config_DeviceConfig_BuzzerMode_MAX meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY
#define _meshtastic_Config_DeviceConfig_BuzzerMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_BuzzerMode)(meshtastic_Config_DeviceConfig_BuzzerMode_SYSTEM_ONLY+1))
#define _meshtastic_Config_DeviceConfig_BuzzerMode_MAX meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY
#define _meshtastic_Config_DeviceConfig_BuzzerMode_ARRAYSIZE ((meshtastic_Config_DeviceConfig_BuzzerMode)(meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY+1))
#define _meshtastic_Config_PositionConfig_PositionFlags_MIN meshtastic_Config_PositionConfig_PositionFlags_UNSET
#define _meshtastic_Config_PositionConfig_PositionFlags_MAX meshtastic_Config_PositionConfig_PositionFlags_SPEED

View File

@@ -6,10 +6,10 @@
#include <pb.h>
#include <vector>
#include "meshtastic/channel.pb.h"
#include "meshtastic/mesh.pb.h"
#include "meshtastic/telemetry.pb.h"
#include "meshtastic/config.pb.h"
#include "meshtastic/localonly.pb.h"
#include "meshtastic/mesh.pb.h"
#include "meshtastic/telemetry.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.

View File

@@ -6,11 +6,11 @@
#include <pb.h>
#include "meshtastic/channel.pb.h"
#include "meshtastic/config.pb.h"
#include "meshtastic/device_ui.pb.h"
#include "meshtastic/module_config.pb.h"
#include "meshtastic/portnums.pb.h"
#include "meshtastic/telemetry.pb.h"
#include "meshtastic/xmodem.pb.h"
#include "meshtastic/device_ui.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
@@ -247,32 +247,26 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_NOMADSTAR_METEOR_PRO = 96,
/* Elecrow CrowPanel Advance models, ESP32-S3 and TFT with SX1262 radio plugin */
meshtastic_HardwareModel_CROWPANEL = 97,
/* *
Lilygo LINK32 board with sensors */
/* Lilygo LINK32 board with sensors */
meshtastic_HardwareModel_LINK_32 = 98,
/* *
Seeed Tracker L1 */
/* Seeed Tracker L1 */
meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1 = 99,
/* *
Seeed Tracker L1 EINK driver */
/* Seeed Tracker L1 EINK driver */
meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1_EINK = 100,
/* Reserved ID for future and past use */
meshtastic_HardwareModel_QWANTZ_TINY_ARMS = 101,
/* *
Lilygo T-Deck Pro */
/* Lilygo T-Deck Pro */
meshtastic_HardwareModel_T_DECK_PRO = 102,
/* *
Lilygo TLora Pager */
/* Lilygo TLora Pager */
meshtastic_HardwareModel_T_LORA_PAGER = 103,
/* *
GAT562 Mesh Trial Tracker */
/* GAT562 Mesh Trial Tracker */
meshtastic_HardwareModel_GAT562_MESH_TRIAL_TRACKER = 104,
/* *
RAKwireless WisMesh Tag */
/* RAKwireless WisMesh Tag */
meshtastic_HardwareModel_WISMESH_TAG = 105,
/* *
RAKwireless WisBlock Core RAK3312 https://docs.rakwireless.com/product-categories/wisduo/rak3112-module/overview/ */
/* RAKwireless WisBlock Core RAK3312 https://docs.rakwireless.com/product-categories/wisduo/rak3112-module/overview/ */
meshtastic_HardwareModel_RAK3312 = 106,
/* Elecrow ThinkNode M5 https://www.elecrow.com/wiki/ThinkNode_M5_Meshtastic_LoRa_Signal_Transceiver_ESP32-S3.html */
meshtastic_HardwareModel_THINKNODE_M5 = 107,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */

View File

@@ -11,7 +11,7 @@
/* Enum definitions */
/* Any significant power changing event in meshtastic should be tagged with a powermon state transition.
If you are making new meshtastic features feel free to add new entries at the end of this definition. */
If you are making new meshtastic features feel free to add new entries at the end of this definition. */
typedef enum _meshtastic_PowerMon_State {
meshtastic_PowerMon_State_None = 0,
meshtastic_PowerMon_State_CPU_DeepSleep = 1,
@@ -34,13 +34,13 @@ something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of
meshtastic_PowerMon_State_Screen_Drawing = 512,
meshtastic_PowerMon_State_Wifi_On = 1024,
/* GPS is actively trying to find our location
See GPSPowerState for more details */
See GPSPowerState for more details */
meshtastic_PowerMon_State_GPS_Active = 2048
} meshtastic_PowerMon_State;
/* What operation would we like the UUT to perform.
note: senders should probably set want_response in their request packets, so that they can know when the state
machine has started processing their request */
note: senders should probably set want_response in their request packets, so that they can know when the state
machine has started processing their request */
typedef enum _meshtastic_PowerStressMessage_Opcode {
/* Unset/unused */
meshtastic_PowerStressMessage_Opcode_UNSET = 0,
@@ -67,7 +67,7 @@ typedef enum _meshtastic_PowerStressMessage_Opcode {
/* Struct definitions */
/* Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs).
But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */
But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */
typedef struct _meshtastic_PowerMon {
char dummy_field;
} meshtastic_PowerMon;

View File

@@ -454,7 +454,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
else if ((destIndex / columns) >= (scrollIndex + visibleRows))
scrollIndex = (destIndex / columns) - visibleRows + 1;
screen->forceDisplay();
screen->forceDisplay(true);
return 1;
}
@@ -469,7 +469,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
if ((destIndex / columns) >= (scrollIndex + visibleRows))
scrollIndex = (destIndex / columns) - visibleRows + 1;
screen->forceDisplay();
screen->forceDisplay(true);
return 1;
}
@@ -491,7 +491,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
runState = returnToCannedList ? CANNED_MESSAGE_RUN_STATE_ACTIVE : CANNED_MESSAGE_RUN_STATE_FREETEXT;
returnToCannedList = false;
screen->forceDisplay();
screen->forceDisplay(true);
return 1;
}
@@ -504,7 +504,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
// UIFrameEvent e;
// e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
// notifyObservers(&e);
screen->forceDisplay();
screen->forceDisplay(true);
return 1;
}
@@ -850,7 +850,13 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
this->waitingForAck = true;
// Log outgoing message
LOG_INFO("Send message id=%d, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
LOG_INFO("Send message id=%u, dest=%x, msg=%.*s", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
if (p->to != 0xffffffff) {
LOG_INFO("Proactively adding %x as favorite node", p->to);
nodeDB->set_favorite(true, p->to);
screen->setFrames(graphics::Screen::FOCUS_PRESERVE);
}
// Send to mesh and phone (even if no phone connected, to track ACKs)
service->sendToMesh(p, RX_SRC_LOCAL, true);
@@ -2077,4 +2083,4 @@ String CannedMessageModule::drawWithCursor(String text, int cursor)
return result;
}
#endif
#endif

View File

@@ -362,9 +362,8 @@ ExternalNotificationModule::ExternalNotificationModule()
if (nodeDB->loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig),
&meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) {
memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone));
strncpy(rtttlConfig.ringtone,
"24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p",
sizeof(rtttlConfig.ringtone));
// The default ringtone is always loaded from userPrefs.jsonc
strncpy(rtttlConfig.ringtone, USERPREFS_RINGTONE_RTTTL, sizeof(rtttlConfig.ringtone));
}
LOG_INFO("Init External Notification Module");

View File

@@ -86,9 +86,11 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
sprintf(message, "Verification: \n");
generateVerificationCode(message + 15);
LOG_INFO("Hash1 matches!");
IF_SCREEN(static const char *optionsArray[] = {"Reject", "Accept"}; graphics::BannerOverlayOptions options;
options.message = message; options.durationMs = 30000; options.optionsArrayPtr = optionsArray;
options.optionsCount = 2; options.notificationType = graphics::notificationTypeEnum::selection_picker;
static const char *optionsArray[] = {"Reject", "Accept"};
// Don't try to put the array definition in the macro. Does not work with curly braces.
IF_SCREEN(graphics::BannerOverlayOptions options; options.message = message; options.durationMs = 30000;
options.optionsArrayPtr = optionsArray; options.optionsCount = 2;
options.notificationType = graphics::notificationTypeEnum::selection_picker;
options.bannerCallback =
[=](int selected) {
if (selected == 1) {

View File

@@ -53,6 +53,7 @@
#endif
#if ARCH_PORTDUINO
#include "input/LinuxInputImpl.h"
#include "input/SeesawRotary.h"
#include "modules/Telemetry/HostMetrics.h"
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
#include "modules/StoreForwardModule.h"
@@ -163,7 +164,6 @@ void setupModules()
// Example: Put your module here
// new ReplyModule();
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
if (!rotaryEncoderInterruptImpl1->init()) {
@@ -189,6 +189,11 @@ void setupModules()
#endif // HAS_BUTTON
#if ARCH_PORTDUINO
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
seesawRotary = new SeesawRotary("SeesawRotary");
if (!seesawRotary->init()) {
delete seesawRotary;
seesawRotary = nullptr;
}
aLinuxInputImpl = new LinuxInputImpl();
aLinuxInputImpl->init();
}

View File

@@ -14,6 +14,11 @@ bool NodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
{
auto p = *pptr;
if (p.is_licensed != owner.is_licensed) {
LOG_WARN("Invalid nodeInfo detected, is_licensed mismatch!");
return true;
}
// Coerce user.id to be derived from the node number
snprintf(p.id, sizeof(p.id), "!%08x", getFrom(&mp));

View File

@@ -266,9 +266,11 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket()
LOG_INFO("Position packet: time=%i lat=%i lon=%i", p.time, p.latitude_i, p.longitude_i);
#ifndef MESHTASTIC_EXCLUDE_ATAK
// TAK Tracker devices should send their position in a TAK packet over the ATAK port
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)
return allocAtakPli();
#endif
return allocDataProtobuf(p);
}

View File

@@ -4,6 +4,7 @@
#include "PowerFSM.h"
#include "buzz.h"
#include "configuration.h"
#include "graphics/Screen.h"
TextMessageModule *textMessageModule;
ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
@@ -17,7 +18,10 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp
devicestate.rx_text_message = mp;
devicestate.has_rx_text_message = true;
powerFSM.trigger(EVENT_RECEIVED_MSG);
// Only trigger screen wake if configuration allows it
if (shouldWakeOnReceivedMessage()) {
powerFSM.trigger(EVENT_RECEIVED_MSG);
}
notifyObservers(&mp);
return ProcessMessage::CONTINUE; // Let others look at this message also if they want

View File

@@ -559,10 +559,8 @@ void MQTT::sendSubscriptions()
int32_t MQTT::runOnce()
{
#if HAS_NETWORKING
if (!moduleConfig.mqtt.enabled || !(moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled()))
return disable();
bool wantConnection = wantsLink();
perhapsReportToMap();
@@ -572,7 +570,7 @@ int32_t MQTT::runOnce()
publishQueuedMessages();
return 200;
}
#if HAS_NETWORKING
else if (!pubSub.loop()) {
if (!wantConnection)
return 5000; // If we don't want connection now, check again in 5 secs
@@ -596,8 +594,10 @@ int32_t MQTT::runOnce()
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE); // Suppress entering light sleep (because that would turn off bluetooth)
return 20;
}
#endif
#else
// No networking available, return default interval
return 30000;
#endif
}
bool MQTT::isValidConfig(const meshtastic_ModuleConfig_MQTTConfig &config, MQTTClient *client)

View File

@@ -49,6 +49,8 @@
#define HW_VENDOR meshtastic_HardwareModel_RAK2560
#elif defined(WISMESH_TAP)
#define HW_VENDOR meshtastic_HardwareModel_WISMESH_TAP
#elif defined(WISMESH_TAG)
#define HW_VENDOR meshtastic_HardwareModel_WISMESH_TAG
#elif defined(GAT562_MESH_TRIAL_TRACKER)
#define HW_VENDOR meshtastic_HardwareModel_GAT562_MESH_TRIAL_TRACKER
#elif defined(RAK4630)
@@ -89,6 +91,8 @@
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_POCKET
#elif defined(NOMADSTAR_METEOR_PRO)
#define HW_VENDOR meshtastic_HardwareModel_NOMADSTAR_METEOR_PRO
#elif defined(SEEED_WIO_TRACKER_L1_EINK)
#define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1_EINK
#elif defined(SEEED_WIO_TRACKER_L1)
#define HW_VENDOR meshtastic_HardwareModel_SEEED_WIO_TRACKER_L1
#else

View File

@@ -35,6 +35,8 @@ char *configPath = nullptr;
char *optionMac = nullptr;
bool forceSimulated = false;
const char *argp_program_version = optstr(APP_VERSION);
// FIXME - move setBluetoothEnable into a HALPlatform class
void setBluetoothEnable(bool enable)
{
@@ -665,6 +667,21 @@ bool loadConfig(const char *configPath)
settingsStrings[hostMetrics_user_command] = (yamlConfig["HostMetrics"]["UserStringCommand"]).as<std::string>("");
}
if (yamlConfig["Config"]) {
if (yamlConfig["Config"]["DisplayMode"]) {
settingsMap[has_configDisplayMode] = true;
if ((yamlConfig["Config"]["DisplayMode"]).as<std::string>("") == "TWOCOLOR") {
settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_TWOCOLOR;
} else if ((yamlConfig["Config"]["DisplayMode"]).as<std::string>("") == "INVERTED") {
settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_INVERTED;
} else if ((yamlConfig["Config"]["DisplayMode"]).as<std::string>("") == "COLOR") {
settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_COLOR;
} else {
settingsMap[configDisplayMode] = meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT;
}
}
}
if (yamlConfig["General"]) {
settingsMap[maxnodes] = (yamlConfig["General"]["MaxNodes"]).as<int>(200);
settingsMap[maxtophone] = (yamlConfig["General"]["MaxMessageQueue"]).as<int>(100);

View File

@@ -109,7 +109,9 @@ enum configNames {
mac_address,
hostMetrics_interval,
hostMetrics_channel,
hostMetrics_user_command
hostMetrics_user_command,
configDisplayMode,
has_configDisplayMode
};
enum { no_screen, x11, fb, st7789, st7735, st7735s, st7796, ili9341, ili9342, ili9486, ili9488, hx8357d };
enum { no_touchscreen, xpt2046, stmpe610, gt911, ft5x06 };

View File

@@ -53,5 +53,7 @@
// "USERPREFS_MQTT_ENCRYPTION_ENABLED": "true",
// "USERPREFS_MQTT_TLS_ENABLED": "false",
// "USERPREFS_MQTT_ROOT_TOPIC": "event/REPLACEME",
// "USERPREFS_RINGTONE_NAG_SECS": "60",
"USERPREFS_RINGTONE_RTTTL": "24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p",
"USERPREFS_TZ_STRING": "tzplaceholder "
}

View File

@@ -9,19 +9,9 @@ build_flags =
-DSERIAL_UART_INSTANCE=1
-DPIN_SERIAL_RX=PA3
-DPIN_SERIAL_TX=PA2
-DHAL_DAC_MODULE_ONLY
-DHAL_RNG_MODULE_ENABLED
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
-DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1
-DMESHTASTIC_EXCLUDE_I2C=1
-DMESHTASTIC_EXCLUDE_WIFI=1
-DMESHTASTIC_EXCLUDE_BLUETOOTH=1
-DMESHTASTIC_EXCLUDE_GPS=1
-DMESHTASTIC_EXCLUDE_SCREEN=1
-DMESHTASTIC_EXCLUDE_MQTT=1
-DMESHTASTIC_EXCLUDE_POWERMON=1
;-DPIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF
;-DCFG_DEBUG

View File

@@ -104,11 +104,11 @@ void setupNicheGraphics()
buttons->setHandlerDown(1, [backlight]() { backlight->peek(); });
buttons->setHandlerLongPress(1, [backlight]() {
backlight->latch();
playBeep();
playBoop();
});
buttons->setHandlerShortPress(1, [backlight]() {
backlight->off();
playBoop();
playChirp();
});
// Begin handling button events

View File

@@ -0,0 +1,24 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
static const uint8_t TX = 21;
static const uint8_t RX = 20;
static const uint8_t SDA = 1;
static const uint8_t SCL = 0;
static const uint8_t SS = 8;
static const uint8_t MOSI = 7;
static const uint8_t MISO = 6;
static const uint8_t SCK = 10;
static const uint8_t A0 = 0;
static const uint8_t A1 = 1;
static const uint8_t A2 = 2;
static const uint8_t A3 = 3;
static const uint8_t A4 = 4;
static const uint8_t A5 = 5;
#endif /* Pins_Arduino_h */

View File

@@ -0,0 +1,61 @@
#ifndef _VARIANT_ESP32C3_SUPER_MINI_
#define _VARIANT_ESP32C3_SUPER_MINI_
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// I2C (Wire) & OLED
#define WIRE_INTERFACES_COUNT (1)
#define I2C_SDA (1)
#define I2C_SCL (0)
#define USE_SSD1306
// GPS
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define GPS_RX_PIN (20)
#define GPS_TX_PIN (21)
// Button
#define BUTTON_PIN (9) // BOOT button
// LoRa
#define USE_LLCC68
#define USE_SX1262
// #define USE_RF95
#define USE_SX1268
#define LORA_DIO0 RADIOLIB_NC
#define LORA_RESET (5)
#define LORA_DIO1 (3)
#define LORA_RXEN (2)
#define LORA_BUSY (4)
#define LORA_SCK (10)
#define LORA_MISO (6)
#define LORA_MOSI (7)
#define LORA_CS (8)
#define SX126X_CS LORA_CS
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_BUSY
#define SX126X_RESET LORA_RESET
#define SX126X_RXEN LORA_RXEN
#define SX126X_DIO3_TCXO_VOLTAGE (1.8)
#define TCXO_OPTIONAL // make it so that the firmware can try both TCXO and XTAL
#ifdef __cplusplus
}
#endif
/*----------------------------------------------------------------------------
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif

View File

@@ -96,6 +96,20 @@ board_level = extra
build_flags = ${env:seeed_xiao_nrf52840_kit.build_flags} -D PRIVATE_HW -DXIAO_BLE_LEGACY_PINOUT -DEBYTE_E22 -DEBYTE_E22_900M30S
build_unflags = -DGPS_L76K
; Seeed XIAO nRF52840 + EBYTE E22-900M30S - Pinout matching Wio-SX1262 (SKU 113010003)
[env:seeed_xiao_nrf52840_e22_900m30s]
extends = env:seeed_xiao_nrf52840_kit
board_level = extra
build_flags = ${env:seeed_xiao_nrf52840_kit.build_flags} -D PRIVATE_HW -DEBYTE_E22 -DEBYTE_E22_900M30S
build_unflags = -DGPS_L76K
; Seeed XIAO nRF52840 + EBYTE E22-900M33S - Pinout matching Wio-SX1262 (SKU 113010003)
[env:seeed_xiao_nrf52840_e22_900m33s]
extends = env:seeed_xiao_nrf52840_kit
board_level = extra
build_flags = ${env:seeed_xiao_nrf52840_kit.build_flags} -D PRIVATE_HW -DEBYTE_E22 -DEBYTE_E22_900M33S
build_unflags = -DGPS_L76K
; Seeed XIAO nRF52840 + XIAO Wio SX1262 DIY
[env:seeed-xiao-nrf52840-wio-sx1262]
board = xiao_ble_sense
@@ -127,3 +141,16 @@ build_flags =
-D ARDUINO_USB_MODE=0
-D ARDUINO_USB_CDC_ON_BOOT=1
-I variants/diy/t-energy-s3_e22
; ESP32 C3 Super Mini Development Board
; https://www.espboards.dev/esp32/esp32-c3-super-mini/
[env:esp32c3_super_mini]
extends = esp32c3_base
board = esp32-c3-devkitm-1
build_flags =
${esp32_base.build_flags}
-D PRIVATE_HW
-I variants/diy/esp32c3_super_mini
-D ARDUINO_USB_MODE=1
-D ARDUINO_USB_CDC_ON_BOOT=1
board_level = extra

View File

@@ -1,264 +1,168 @@
#
# XIAO nrf52840/nrf52840 Sense + Ebyte E22-900M30S
<p align="center" style="font-size: 28px;">
Xiao BLE/BLE Sense + Ebyte E22-900M30S
</p>
<p align="center" style="font-size: 20px;">
A step-by-step guide for macOS and Linux
</p>
_A step-by-step guide for macOS and Linux._
## Introduction
This guide will walk you through everything needed to get the Xiao BLE (or BLE Sense) running Meshtastic using an Ebyte E22-900M30S LoRa module. The combination of the E22 with an nRF52840 MCU is desirable because it allows for both very low idle (Rx) power draw <i>and</i> high transmit power. The Xiao BLE is a small but surprisingly well-appointed nRF52840 board, with enough GPIO for most Meshtastic applications and a built-in LiPo charger. The E22, on the other hand, is a famously inscrutable and mysterious beast. It is one of the more readily available LoRa modules capable of transmitting at 30 dBm, and includes an LNA to boost its Rx sensitivity a few dB beyond that of the SX1262. However, its documentation is relatively sparse overall, and seems to merely hint at (or completely omit) several key details regarding its functionality. Thus, much of what follows is a synthesis of my observations and inferences over the course of many hours of trial and error.
This guide will walk you through everything needed to get the XIAO nrf52840 (or XIAO nrf52840 Sense) running Meshtastic using an Ebyte E22-900M30S LoRa module. The combination of the E22 with an nRF52840 MCU is desirable because it allows for both very low idle (Rx) power draw _and_ high transmit power.
<h3>Acknowledgement and friendly disclaimer</h3>
The XIAO nrf52840 is a small but surprisingly well-appointed nRF52840 board, with enough GPIO for most Meshtastic applications and a built-in LiPo charger.
The E22, on the other hand, is a famously inscrutable and mysterious beast. It is one of the more readily available LoRa modules capable of transmitting at 30 dBm, and includes an LNA to boost its Rx sensitivity a few dB beyond that of the SX1262.
However, its documentation is relatively sparse overall, and seems to merely hint at (or completely omit) several key details regarding its functionality. Thus, much of what follows is a synthesis of my observations and inferences over the course of many hours of trial and error.
### Acknowledgement and Friendly Disclaimer
Huge thanks to those in the community who have forged the way with the E22, without whose hard work none of this would have been possible! (thebentern, riddick, rainer_vie, beegee-tokyo, geeksville, caveman99, Der_Bear, PlumRugOfDoom, BigCorvus, and many others.)
<br>
Please take the conclusions here as a tentative work in progress, representing my current (and fairly limited) understanding of the E22 when paired with this particular MCU. It is my hope that this guide will be helpful to others who are interested in trying a DIY Meshtastic build, and also be subject to revision by folks with more experience and better test equipment.
### Obligatory liability disclaimer
### Obligatory Liability Disclaimer
This guide and all associated content is for informational purposes only. The information presented is intended for consumption only by persons having appropriate technical skill and judgement, to be used entirely at their own discretion and risk. The authors of this guide in no way provide any warranty, express or implied, toward the content herein, nor its correctness, safety, or suitability to any particular purpose. By following the instructions in this guide in part or in full, you assume all responsibility for all potential risks, including but not limited to fire, property damage, bodily injury, and death.
### Note
## 1. Wire the board
These instructions assume you are running macOS or Linux, but it should be relatively easy to translate each command for Windows. (In this case, in step 2 below, each line of `xiao_ble.sh` would also need to be converted to the equivalent Windows CLI command and run individually.)
Connecting the E22 to the XIAO nrf52840 is straightforward, but there are a few gotchas to be mindful of.
## 1. Update Bootloader
### On the XIAO nrf52840
The first thing you will need to do is update the Xiao BLE's bootloader. The stock bootloader is functionally very similar to the Adafruit nRF52 UF2 bootloader, but apparently not quite enough so to work with Meshtastic out of the box.
- Pins D4 and D5 are currently mapped to `PIN_WIRE_SDA` and `PIN_WIRE_SCL`, respectively. If you are not using I²C and would like to free up pins D4 and D5 for use as GPIO, `PIN_WIRE_SDA` and `PIN_WIRE_SCL` can be reassigned to any two other unused pin numbers.
- Pins D6 and D7 were originally mapped to the TX and RX pins for serial interface 1 (`PIN_SERIAL1_RX` and `PIN_SERIAL1_TX`) but are currently set to -1 in `variant.h`. If you need to expose a serial interface, you can restore these pins and move e.g. `SX126X_RXEN` to pin 4 or 5 (the opposite should work too).
1. Connect the Xiao BLE to your computer via USB-C.
### On the E22
2. Install `adafruit-nrfutil` by following the instructions <a href="https://github.com/adafruit/Adafruit_nRF52_nrfutil">here</a>.
- There are two options for the E22's `TXEN` pin:
1. It can be connected to the MCU on the pin defined as `SX126X_TXEN` in `variant.h`. In this configuration, the MCU will control Tx/Rx switching "manually". As long as `SX126X_TXEN` and `SX126X_RXEN` are both defined in `variant.h` (and neither is set to `RADIOLIB_NC`), `SX126xInterface.cpp` will initialize the E22 correctly for this mode.
2. Alternately, it can be connected to the E22's `DIO2` pin only, with neither `TXEN` nor `DIO2` being connected to the MCU. In this configuration, the E22 will control Tx/Rx switching automatically. In `variant.h`, as long as `SX126X_TXEN` is defined as `RADIOLIB_NC`, and `SX126X_RXEN` is defined and connected to the E22's `RXEN` pin, and `E22_TXEN_CONNECTED_TO_DIO2` is defined, `SX126xInterface.cpp` will initialize the E22 correctly for this mode. This configuration frees up a GPIO, and presents no drawbacks that I have found.
- Note that any combination other than the two described above will likely result in unexpected behavior. In my testing, some of these other configurations appeared to "work" at first glance, but every one I tried had at least one of the following flaws: weak Tx power, extremely poor Rx sensitivity, or the E22 overheating because TXEN was never pulled low, causing its PA to stay on indefinitely.
- Along the same lines, it is a good idea to check the E22's temperature frequently by lightly touching the shield. If you feel the shield getting hot (i.e. approaching uncomfortable to touch) near pins 1, 2, and 3, something is probably misconfigured; disconnect both the XIAO nrf52840 and E22 from power and double check wiring and pin mapping.
- Whether you opt to let the E22 control Rx and Tx or handle this manually, **the E22's `RXEN` pin must always be connected to the MCU** on the pin defined as `SX126X_RXEN` in `variant.h`.
3. Open a terminal window and navigate to `firmware/variants/xiao_ble` (where `firmware` is the directory into which you have cloned the <a href="https://github.com/meshtastic/firmware">Meshtastic firmware repo</a>).
#### Note
4. Run the following command, replacing `/dev/cu.usbmodem2101` with the serial port your Xiao BLE is connected to:
The default pin mapping in `variant.h` uses "Automatic Tx/Rx switching" mode.
```bash
adafruit-nrfutil --verbose dfu serial --package xiao_nrf52840_ble_bootloader-0.7.0-22-g277a0c8_s140_7.3.0.zip --port /dev/cu.usbmodem2101 -b 115200 --singlebank --touch 1200
```
If you wire your board for Manual Tx/Rx Switching Mode, `SX126X_TXEN` must be defined (`#define #define SX126X_TXEN D6`) in `variants/seeed_xiao_nrf52840_kit/variant.h` in the code block following:
5. If all goes well, the Xiao BLE's red LED should start to pulse slowly, and you should see a new USB storage device called `XIAO-BOOT` appear under `Locations` in Finder.
```c
#ifdef XIAO_BLE_LEGACY_PINOUT
// Legacy xiao_ble variant pinout for third-party SX126x modules e.g. EBYTE E22
```
&nbsp;
### Example Wiring for Automatic Tx/Rx Switching Mode
## 2. PlatformIO Environment Preparation
#### MCU -> E22 Connections
Before building Meshtastic for the Xiao BLE + E22, it is necessary to pull in SoftDevice 7.3.0 and its associated linker script (nrf52840_s140_v7.ld) from Seeed Studio's Arduino core. The `xiao_ble.sh` script does this.
| XIAO nrf52840 pin | variant.h definition | E22 pin | Notes |
| :---------------- | :------------------- | :-------- | :------------------------------------------------------------------------------------------------------------------- |
| D0 | SX126X_CS | 19 (NSS) | |
| D1 | SX126X_DIO1 | 13 (DIO1) | |
| D2 | SX126X_BUSY | 14 (BUSY) | |
| D3 | SX126X_RESET | 15 (NRST) | |
| D7 | SX126X_RXEN | 6 (RXEN) | These pins must still be connected, and `SX126X_RXEN` defined in `variant.h`, otherwise Rx sensitivity will be poor. |
| D8 | PIN_SPI_SCK | 18 (SCK) | |
| D9 | PIN_SPI_MISO | 16 (MISO) | |
| D10 | PIN_SPI_MOSI | 17 (MOSI) | |
1. In your terminal window, run the following command:
```bash
sudo ./xiao_ble.sh
```
&nbsp;
## 3. Build Meshtastic
At this point, you should be able to build the firmware successfully.
1. In VS Code, press `Command Shift P` to bring up the command palette.
2. Search for and run the `Developer: Reload Window` command.
3. Bring up the command palette again with `Command Shift P`. Search for and run the `PlatformIO: Pick Project Environment` command.
4. In the list of environments, select `env:xiao_ble`. PlatformIO may update itself for a minute or two, and should let you know once done.
5. Return to the command palette once again (`Command Shift P`). Search for and run the `PlatformIO: Build` command.
6. PlatformIO will build the project. After a few minutes you should see a green `SUCCESS` message.
&nbsp;
## 4. Wire the board
Connecting the E22 to the Xiao BLE is straightforward, but there are a few gotchas to be mindful of.
- <strong>On the Xiao BLE:</strong>
- Pins D4 and D5 are currently mapped to `PIN_WIRE_SDA` and `PIN_WIRE_SCL`, respectively. If you are not using I²C and would like to free up pins D4 and D5 for use as GPIO, `PIN_WIRE_SDA` and `PIN_WIRE_SCL` can be reassigned to any two other unused pin numbers.
- Pins D6 and D7 were originally mapped to the TX and RX pins for serial interface 1 (`PIN_SERIAL1_RX` and `PIN_SERIAL1_TX`) but are currently set to -1 in `variant.h`. If you need to expose a serial interface, you can restore these pins and move e.g. `SX126X_RXEN` to pin 4 or 5 (the opposite should work too).
- <strong>On the E22:</strong>
- There are two options for the E22's `TXEN` pin:
1. It can be connected to the MCU on the pin defined as `SX126X_TXEN` in `variant.h`. In this configuration, the MCU will control Tx/Rx switching "manually". As long as `SX126X_TXEN` and `SX126X_RXEN` are both defined in `variant.h` (and neither is set to `RADIOLIB_NC`), `SX126xInterface.cpp` will initialize the E22 correctly for this mode.
2. Alternately, it can be connected to the E22's `DIO2` pin only, with neither `TXEN` nor `DIO2` being connected to the MCU. In this configuration, the E22 will control Tx/Rx switching automatically. In `variant.h`, as long as `SX126X_TXEN` is defined as `RADIOLIB_NC`, and `SX126X_RXEN` is defined and connected to the E22's `RXEN` pin, and `E22_TXEN_CONNECTED_TO_DIO2` is defined, `SX126xInterface.cpp` will initialize the E22 correctly for this mode. This configuration frees up a GPIO, and presents no drawbacks that I have found.
- Note that any combination other than the two described above will likely result in unexpected behavior. In my testing, some of these other configurations appeared to "work" at first glance, but every one I tried had at least one of the following flaws: weak Tx power, extremely poor Rx sensitivity, or the E22 overheating because TXEN was never pulled low, causing its PA to stay on indefinitely.
- Along the same lines, it is a good idea to check the E22's temperature frequently by lightly touching the shield. If you feel the shield getting hot (i.e. approaching uncomfortable to touch) near pins 1, 2, and 3, something is probably misconfigured; disconnect both the Xiao BLE and E22 from power and double check wiring and pin mapping.
- Whether you opt to let the E22 control Rx and Tx or handle this manually, <strong>the E22's `RXEN` pin must always be connected to the MCU</strong> on the pin defined as `SX126X_RXEN` in `variant.h`.
<h3>Note</h3>
The default pin mapping in `variant.h` uses 'automatic Tx/Rx switching' mode. If you wire your board for manual Rx/Tx switching, make sure to update `variant.h` accordingly by commenting/uncommenting the necessary lines in the 'E22 Tx/Rx control options' section.
&nbsp;
---
&nbsp;
<h3>Example wiring for "E22 automatic Tx/Rx switching" mode:</h3>
&nbsp;
<strong>MCU -> E22 connections</strong>
| Xiao BLE pin | variant.h definition | E22 pin | Notes |
| :----------- | :------------------- | :-------- | :------------------------------------------------------------------------------------------------------------------- |
| D0 | SX126X_CS | 19 (NSS) | |
| D1 | SX126X_DIO1 | 13 (DIO1) | |
| D2 | SX126X_BUSY | 14 (BUSY) | |
| D3 | SX126X_RESET | 15 (NRST) | |
| D7 | SX126X_RXEN | 6 (RXEN) | These pins must still be connected, and `SX126X_RXEN` defined in `variant.h`, otherwise Rx sensitivity will be poor. |
| D8 | PIN_SPI_SCK | 18 (SCK) | |
| D9 | PIN_SPI_MISO | 16 (MISO) | |
| D10 | PIN_SPI_MOSI | 17 (MOSI) | |
&nbsp;
&nbsp;
<strong>E22 -> E22 connections:</strong>
#### E22 -> E22 Connections
| E22 pin | E22 pin | Notes |
| :------ | :------ | :------------------------------------------------------------------------ |
| TXEN | DIO2 | These must be physically connected for automatic Tx/Rx switching to work. |
<h3>Note</h3>
#### Note
The schematic (`xiao-ble-e22-schematic.png`) in the `eagle-project` directory uses this wiring.
&nbsp;
### Example Wiring for Manual Tx/Rx Switching Mode
---
#### MCU -> E22 Connections
&nbsp;
| XIAO nrf52840 pin | variant.h definition | E22 pin | Notes |
| :---------------- | :------------------- | :-------- | :---- |
| D0 | SX126X_CS | 19 (NSS) | |
| D1 | SX126X_DIO1 | 13 (DIO1) | |
| D2 | SX126X_BUSY | 14 (BUSY) | |
| D3 | SX126X_RESET | 15 (NRST) | |
| D6 | SX126X_TXEN | 7 (TXEN) | |
| D7 | SX126X_RXEN | 6 (RXEN) | |
| D8 | PIN_SPI_SCK | 18 (SCK) | |
| D9 | PIN_SPI_MISO | 16 (MISO) | |
| D10 | PIN_SPI_MOSI | 17 (MOSI) | |
<h3>Example wiring for "Manual Tx/Rx switching" mode:</h3>
#### E22 -> E22 connections
<strong>MCU -> E22 connections</strong>
_(none)_
| Xiao BLE pin | variant.h definition | E22 pin | Notes |
| :----------- | :------------------- | :-------- | :---- |
| D0 | SX126X_CS | 19 (NSS) | |
| D1 | SX126X_DIO1 | 13 (DIO1) | |
| D2 | SX126X_BUSY | 14 (BUSY) | |
| D3 | SX126X_RESET | 15 (NRST) | |
| D6 | SX126X_TXEN | 7 (TXEN) | |
| D7 | SX126X_RXEN | 6 (RXEN) | |
| D8 | PIN_SPI_SCK | 18 (SCK) | |
| D9 | PIN_SPI_MISO | 16 (MISO) | |
| D10 | PIN_SPI_MOSI | 17 (MOSI) | |
## 2. Build Meshtastic
<strong>E22 -> E22 connections:</strong> (none)
1. Follow the [Building Meshtastic Firmware](https://meshtastic.org/docs/development/firmware/build/) documentation, stop after **Build****Step 2**
2. For **Build****Step 3**, select `xiao_ble` as your target
3. Adjust source code if you:
- Wired your board for Manual Tx/Rx Switching Mode: see [Wire the Board](#1-wire-the-board)
- Used an E22-900M33S module
(this step is important to avoid **damaging the power amplifier** in the M33S module and **transmitting power above legal limits**!):
1. Open `variants/diy/platformio.ini`
2. Search for `[env:xiao_ble]`
3. In the line starting with `build_flags` within this section, change `-DEBYTE_E22_900M30S` to `-DEBYTE_E22_900M33S`
4. Follow **Build****Step 4** to build the firmware
5. Stop here, because the **PlatformIO: Upload** step does not work for factory-fresh XIAO nrf52840 (the automatic reset to bootloader only works if Meshtastic firmware is already running)
6. The built `firmware.uf2` binary can be found in the folder `.pio/build/xiao_ble/firmware.uf2` (relative to where you cloned the Git repository to), we will need it for [flashing the firmware](#3-flash-the-firmware-to-the-xiao-nrf52840) (manually)
&nbsp;
## 3. Flash the Firmware to the XIAO nrf52840
## 5. Flash the firmware to the Xiao BLE
1. Double press the XIAO nrf52840's `reset` button to put it in bootloader mode, and a USB volume named `XIAO SENSE` will appear
2. Copy the `firmware.uf2` file to the `XIAO SENSE` volume (refer to the last step of [Build Meshtastic](#2-build-meshtastic))
3. The XIAO nrf52840's red LED will flash for several seconds as the firmware is copied
4. Once Meshtastic firmware succesfully boots, the:
1. Green LED will turn on
2. Red LED will flash several times to indicate flash memory writes during initial settings file creation
3. Green LED will blink every second once the firmware is running normally
5. If you do not see the above LED patters, proceed to [Troubleshooting](#4-troubleshooting)
1. Double press the Xiao's `reset` button to put it in bootloader mode.
2. In a terminal window, navigate to the Meshtastic firmware repo's root directory, and from there to `.pio/build/xiao_ble`.
3. Convert the generated `.hex` file into a `.uf2` file:
```bash
../../../bin/uf2conv.py firmware.hex -c -o firmware.uf2 -f 0xADA52840
```
4. Copy the new `.uf2` file to the Xiao's mass storage volume:
```bash
cp firmware.uf2 /Volumes/XIAO-BOOT
```
5. The Xiao's red LED will flash for several seconds as the firmware is copied.
6. Once the firmware is copied, to verify it is running, run the following command:
```bash
meshtastic --noproto
```
7. Then, press the Xiao's `reset` button again. You should see a lot of debug output logged in the terminal window.
&nbsp;
## 6. Troubleshooting
- If after flashing Meshtastic, the Xiao is bootlooped, look at the serial output (you can see this by running `meshtastic --noproto` with the device connected to your computer via USB).
## 4. Troubleshooting
- If after flashing Meshtastic, the XIAO is bootlooped, look at the serial output (you can see this by running `meshtastic --noproto` with the device connected to your computer via USB).
- If you see that the SX1262 init result was -2, this likely indicates a wiring problem; double check your wiring and pin mapping in `variant.h`.
- If you see an error mentioning tinyFS, this may mean you need to reformat the Xiao's storage:
1. Double press the `reset` button to put the Xiao in bootloader mode.
2. In a terminal window, navigate to the Meshtastic firmware repo's root directory, and from there to `variants/xiao_ble`.
3. Run the following command: &nbsp;`cp xiao-ble-internal-format.uf2 /Volumes/XIAO-BOOT`
4. The Xiao's red LED will flash briefly as the filesystem format firmware is copied.
5. Run the following command: &nbsp;`meshtastic --noproto`
6. In the output of the above command, you should see a message saying "Formatting...done".
7. To flash Meshtastic again, repeat the steps in section 5 above.
- If you see an error mentioning tinyFS, this may mean you need to reformat the XIAO's storage:
1. Open the [Meshtastic web flasher](https://flasher.meshtastic.org/)
2. Select the **_Seeed XIAO NRF52840 Kit_**
3. Click the **_trash can icon_** to the right of **_Flash_**
4. Follow the instructions on the screen
**Do not flash the Seeed XIAO NRF52840 Kit firmware** if you have wired the LoRa module according to this variant, as the Seeed XIAO NRF52840 Kit uses different wiring for the SX1262 LoRa chip
- If you don't see any specific error message, but the boot process is stuck or not proceeding as expected, this might also mean there is a conflict in `variant.h`. If you have made any changes to the pin mapping, ensure they do not result in a conflict. If all else fails, try reverting your changes and using the known-good configuration included here.
- The above might also mean something is wired incorrectly. Try reverting to one of the known-good example wirings in section 4.
- If the E22 gets hot to the touch:
- The power amplifier is likely running continually. Disconnect it and the Xiao from power immediately, and double check wiring and pin mapping. In my experimentation this occurred in cases where TXEN was inadvertenly high (usually due to a pin mapping conflict).
- The power amplifier is likely running continually. Disconnect it and the XIAO from power immediately, and double check wiring and pin mapping. In my experimentation this occurred in cases where TXEN was inadvertenly high (usually due to a pin mapping conflict).
&nbsp;
## 5. Notes
## 7. Notes
- **Transmit Power**
- There is a power amplifier after the SX1262's Tx, so the actual Tx power is just over 7 dB greater than the SX1262's set Tx power (the E22-900M30S actually tops out just over 29dB at 5V according to the datasheet)
- Meshtastic firmware is aware of the gain of the E22-900M30S module, so the Meshtastic clients' Tx power setting reflects the actual output power, i.e. setting 30 dBm in the Meshtastic app programs the E22 module to correctly output 30 dBm, setting 24 dBm will output 24 dBm, etc.
- **Adequate 5V Power Supply to the E22 Module**
- Have a bypass capacitor from its 5V supply to ground; 100 µF works well
- Voltage must be between 5V5.5V, lower supply voltage results in less output power; for example, with a fully charged LiPo at 4.2V, Tx power appears to max out around 26-27 dBm
- There are several anecdotal recommendations regarding the Tx power the E22's internal SX1262 should be set to in order to achieve the advertised output of 30 dBm, ranging from 4 (per <a href="https://github.com/jgromes/RadioLib/wiki/High-power-Radio-Modules-Guide">this article</a> in the RadioLib github repo) to 22 (per <a href="https://discord.com/channels/867578229534359593/871539930852130866/976472577545490482">this conversation</a> from the Meshtastic Discord). When paired with the Xiao BLE in the configurations described above, I observed that the output is at its maximum when Tx power is set to 22.
### Additional Reading
- To achieve its full output, the E22 should have a bypass capacitor from its 5V supply to ground. 100 µF works well.
- [S5NC/CDEBYTE_Modules](https://github.com/S5NC/CDEBYTE_Modules) has additional information about EBYTE E22 modules' internal workings, including photographs
- [RadioLib High power Radio Modules Guide](https://github.com/jgromes/RadioLib/wiki/High-power-Radio-Modules-Guide)
- The E22 will happily run on voltages lower than 5V, but the full output power will not be realized. For example, with a fully charged LiPo at 4.2V, Tx power appears to max out around 26-27 dBm.
&nbsp;
## 8. Testing Methodology
## 6. Testing Methodology
During what became a fairly long trial-and-error process, I did a lot of careful testing of Tx power and Rx sensitivity. My methodology in these tests was as follows:
- All tests were conducted between two nodes:
1. The Xiao BLE + E22 coupled with an <a href="https://www.digikey.com/en/products/detail/abracon-llc/ARRKP4065-S915A/8593263">Abracon ARRKP4065-S915A</a> ceramic patch antenna
2. A RAK 5005/4631 coupled with a <a href="https://www.streakwave.com/laird-technologies-ma9-5n-55dbi-900mhz-mobile-omni-select-mount">Laird MA9-5N</a> antenna via a 4" U.FL to Type N pigtail.
1. The XIAO nrf52840 + E22 coupled with an [Abracon ARRKP4065-S915A](https://www.digikey.com/en/products/detail/abracon-llc/ARRKP4065-S915A/8593263") ceramic patch antenna
2. A RAK 5005/4631 coupled with a [Laird MA9-5N](https://www.streakwave.com/laird-technologies-ma9-5n-55dbi-900mhz-mobile-omni-select-mount) antenna via a 4" U.FL to Type N pigtail.
- No other nodes were powered up onsite or nearby.
<br>
- Each node and its antenna was kept in exactly the same position and orientation throughout testing.
- Other environmental factors (e.g. the location and resting position of my body in the room while testing) were controlled as carefully as possible.
- Each test comprised at least five (and often ten) runs, after which the results were averaged.
- All testing was done by sending single-character messages between nodes and observing the received RSSI reported in the message acknowledgement. Messages were sent one by one, waiting for each to be acknowledged or time out before sending the next.
- The E22's Tx power was observed by sending messages from the RAK to the Xiao BLE + E22 and recording the received RSSI.
- The opposite was done to observe the E22's Rx sensitivity: messages were sent from the Xiao BLE + E22 to the RAK, and the received RSSI was recorded.
While this cannot match the level of accuracy achievable with actual test equipment in a lab setting, it was nonetheless sufficient to demonstrate the (sometimes very large) differences in Tx power and Rx sensitivity between various configurations.
- The E22's Tx power was observed by sending messages from the RAK to the XIAO nrf52840 + E22 and recording the received RSSI.
- The opposite was done to observe the E22's Rx sensitivity: messages were sent from the XIAO nrf52840 + E22 to the RAK, and the received RSSI was recorded.
While this cannot match the level of accuracy achievable with actual test equipment in a lab setting, it was nonetheless sufficient to demonstrate the (sometimes very large) differences in Tx power and Rx sensitivity between various configurations.

View File

@@ -107,7 +107,7 @@ void setupNicheGraphics()
buttons->setWiring(1, PIN_BUTTON2);
buttons->setHandlerShortPress(1, [inkhud]() {
inkhud->nextTile();
playBoop();
playChirp();
});
// Begin handling button events

View File

@@ -104,7 +104,7 @@ void setupNicheGraphics()
buttons->setWiring(1, PIN_BUTTON2);
buttons->setHandlerShortPress(1, [inkhud]() {
inkhud->nextTile();
playBoop();
playChirp();
});
// Begin handling button events

View File

@@ -222,6 +222,7 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
// #define PIN_GPS_EN PIN_3V3_EN
#define PIN_GPS_PPS (17) // Pulse per second input from the GPS
#define GPS_SERIAL_PORT Serial2
// On RAK2560 the GPS is be on a different UART
// #define GPS_RX_PIN PIN_SERIAL2_RX
// #define GPS_TX_PIN PIN_SERIAL2_TX

View File

@@ -7,18 +7,8 @@ build_flags =
-Ivariants/rak3172
-DPIN_WIRE_SDA=PA11
-DPIN_WIRE_SCL=PA12
-DHAL_DAC_MODULE_ONLY
-DHAL_RNG_MODULE_ENABLED
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
-DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR=1
-DMESHTASTIC_EXCLUDE_I2C=1
-DMESHTASTIC_EXCLUDE_WIFI=1
-DMESHTASTIC_EXCLUDE_BLUETOOTH=1
-DMESHTASTIC_EXCLUDE_GPS=1
-DMESHTASTIC_EXCLUDE_SCREEN=1
-DMESHTASTIC_EXCLUDE_MQTT=1
-DMESHTASTIC_EXCLUDE_POWERMON=1
;-DCFG_DEBUG
upload_port = stlink

View File

@@ -0,0 +1,15 @@
; The very slick RAK wireless RAK 4631 / 4630 board - Unified firmware for 5005/19003, with or without OLED RAK 1921
[env:rak_wismeshtag]
extends = nrf52840_base
board = wiscore_rak4631
board_check = true
build_flags = ${nrf52840_base.build_flags} -Ivariants/rak_wismeshtag -D WISMESH_TAG -D RAK_4631
-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.
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
-DMESHTASTIC_EXCLUDE_WIFI=1
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak_wismeshtag>
lib_deps =
${nrf52840_base.lib_deps}

View File

@@ -0,0 +1,45 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "variant.h"
#include "nrf.h"
#include "wiring_constants.h"
#include "wiring_digital.h"
const uint32_t g_ADigitalPinMap[] = {
// P0
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
// P1
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47};
void initVariant()
{
// LED1 & LED2
pinMode(PIN_LED1, OUTPUT);
ledOff(PIN_LED1);
pinMode(PIN_LED2, OUTPUT);
ledOff(PIN_LED2);
// 3V3 Power Rail
pinMode(PIN_3V3_EN, OUTPUT);
digitalWrite(PIN_3V3_EN, HIGH);
}

View File

@@ -0,0 +1,245 @@
/*
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
Copyright (c) 2016 Sandeep Mistry All right reserved.
Copyright (c) 2018, Adafruit Industries (adafruit.com)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _VARIANT_RAK4630_
#define _VARIANT_RAK4630_
#define RAK4630
/** Master clock frequency */
#define VARIANT_MCK (64000000ul)
#define USE_LFXO // Board uses 32khz crystal for LF
// define USE_LFRC // Board uses RC for LF
/*----------------------------------------------------------------------------
* Headers
*----------------------------------------------------------------------------*/
#include "WVariant.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// Number of pins defined in PinDescription array
#define PINS_COUNT (48)
#define NUM_DIGITAL_PINS (48)
#define NUM_ANALOG_INPUTS (6)
#define NUM_ANALOG_OUTPUTS (0)
// LEDs
#define PIN_LED1 (35)
#define PIN_LED2 (36)
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
#define LED_GREEN PIN_LED1
#define LED_BLUE PIN_LED2
#define LED_STATE_ON 1 // State when LED is litted
/*
* Buttons
*/
#define PIN_BUTTON1 9 // Pin for button on E-ink button module or IO expansion
#define BUTTON_NEED_PULLUP
#define PIN_BUTTON2 12
#define PIN_BUTTON3 24
#define PIN_BUTTON4 25
/*
* Analog pins
*/
#define PIN_A0 (5)
#define PIN_A1 (31)
#define PIN_A2 (28)
#define PIN_A3 (29)
#define PIN_A4 (30)
#define PIN_A5 (31)
#define PIN_A6 (0xff)
#define PIN_A7 (0xff)
static const uint8_t A0 = PIN_A0;
static const uint8_t A1 = PIN_A1;
static const uint8_t A2 = PIN_A2;
static const uint8_t A3 = PIN_A3;
static const uint8_t A4 = PIN_A4;
static const uint8_t A5 = PIN_A5;
static const uint8_t A6 = PIN_A6;
static const uint8_t A7 = PIN_A7;
#define ADC_RESOLUTION 14
// Other pins
#define PIN_AREF (2)
#define PIN_NFC1 (9)
#define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
/*
* Serial interfaces
*/
#define PIN_SERIAL1_RX (15)
#define PIN_SERIAL1_TX (16)
// Connected to Jlink CDC
#define PIN_SERIAL2_RX (8)
#define PIN_SERIAL2_TX (6)
/*
* SPI Interfaces
*/
#define SPI_INTERFACES_COUNT 2
#define PIN_SPI_MISO (45)
#define PIN_SPI_MOSI (44)
#define PIN_SPI_SCK (43)
#define PIN_SPI1_MISO (29) // (0 + 29)
#define PIN_SPI1_MOSI (30) // (0 + 30)
#define PIN_SPI1_SCK (3) // (0 + 3)
static const uint8_t SS = 42;
static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK = PIN_SPI_SCK;
/*
* eink display pins
*/
#define PIN_EINK_CS (0 + 26)
#define PIN_EINK_BUSY (0 + 4)
#define PIN_EINK_DC (0 + 17)
#define PIN_EINK_RES (-1)
#define PIN_EINK_SCLK (0 + 3)
#define PIN_EINK_MOSI (0 + 30) // also called SDI
/*
* Wire Interfaces
*/
#define WIRE_INTERFACES_COUNT 1
// RAK WISMESHTAG
#define PIN_WIRE_SDA (25)
#define PIN_WIRE_SCL (24)
// QSPI Pins
#define PIN_QSPI_SCK 3
#define PIN_QSPI_CS 26
#define PIN_QSPI_IO0 30
#define PIN_QSPI_IO1 29
#define PIN_QSPI_IO2 28
#define PIN_QSPI_IO3 2
/* @note RAK5005-O GPIO mapping to RAK4631 GPIO ports
RAK5005-O <-> nRF52840
IO1 <-> P0.17 (Arduino GPIO number 17)
IO2 <-> P1.02 (Arduino GPIO number 34)
IO3 <-> P0.21 (Arduino GPIO number 21)
IO4 <-> P0.04 (Arduino GPIO number 4)
IO5 <-> P0.09 (Arduino GPIO number 9)
IO6 <-> P0.10 (Arduino GPIO number 10)
IO7 <-> P0.28 (Arduino GPIO number 28)
SW1 <-> P0.01 (Arduino GPIO number 1)
A0 <-> P0.04/AIN2 (Arduino Analog A2
A1 <-> P0.31/AIN7 (Arduino Analog A7
SPI_CS <-> P0.26 (Arduino GPIO number 26)
*/
// RAK4630 LoRa module
/* Setup of the SX1262 LoRa module ( https://docs.rakwireless.com/Product-Categories/WisBlock/RAK4631/Datasheet/ )
P1.10 NSS SPI NSS (Arduino GPIO number 42)
P1.11 SCK SPI CLK (Arduino GPIO number 43)
P1.12 MOSI SPI MOSI (Arduino GPIO number 44)
P1.13 MISO SPI MISO (Arduino GPIO number 45)
P1.14 BUSY BUSY signal (Arduino GPIO number 46)
P1.15 DIO1 DIO1 event interrupt (Arduino GPIO number 47)
P1.06 NRESET NRESET manual reset of the SX1262 (Arduino GPIO number 38)
Important for successful SX1262 initialization:
* Setup DIO2 to control the antenna switch
* Setup DIO3 to control the TCXO power supply
* Setup the SX1262 to use it's DCDC regulator and not the LDO
* RAK4630 schematics show GPIO P1.07 connected to the antenna switch, but it should not be initialized, as DIO2 will do the
control of the antenna switch
SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
*/
#define DETECTION_SENSOR_EN 4
#define USE_SX1262
#define SX126X_CS (42)
#define SX126X_DIO1 (47)
#define SX126X_BUSY (46)
#define SX126X_RESET (38)
// #define SX126X_TXEN (39)
// #define SX126X_RXEN (37)
#define SX126X_POWER_EN (37)
// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
#define SX126X_DIO2_AS_RF_SWITCH
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// Testing USB detection
#define NRF_APM
// enables 3.3V periphery like GPS or IO Module
// Do not toggle this for GPS power savings
#define PIN_3V3_EN (34)
// RAK WISMESHTAG
#define PIN_GPS_EN PIN_3V3_EN
#define PIN_GPS_PPS (17) // Pulse per second input from the GPS
#define GPS_RX_PIN PIN_SERIAL1_RX
#define GPS_TX_PIN PIN_SERIAL1_TX
// RAK WISMESHTAG
#define PIN_BUZZER 21
// Battery
// The battery sense is hooked to pin A0 (5)
#define BATTERY_PIN PIN_A0
// and has 12 bit resolution
#define BATTERY_SENSE_RESOLUTION_BITS 12
#define BATTERY_SENSE_RESOLUTION 4096.0
#undef AREF_VOLTAGE
#define AREF_VOLTAGE 3.0
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER 1.73
#define RAK_4631 1
#ifdef __cplusplus
}
#endif
/*----------------------------------------------------------------------------
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
#endif

View File

@@ -4,7 +4,8 @@ extends = nrf52840_base, inkhud
;board_level = extra
build_flags = ${nrf52840_base.build_flags} ${inkhud.build_flags}
-I $PROJECT_DIR/variants/seeed_wio_tracker_L1_eink
-D SEEED_WIO_TRACKER_L1
-D SEEED_WIO_TRACKER_L1_EINK
-D SEEED_WIO_TRACKER_L1
-Isrc/platform/nrf52/softdevice -Isrc/platform/nrf52/softdevice/nrf52
board_build.ldscript = src/platform/nrf52/nrf52840_s140_v7.ld
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/seeed_wio_tracker_L1_eink> ${inkhud.build_src_filter}

View File

@@ -1,4 +1,4 @@
; Seeed Xiao BLE: https://www.digikey.com/en/products/detail/seeed-technology-co-ltd/102010448/16652893
; Seeed Xiao BLE: https://wiki.seeedstudio.com/XIAO_BLE/
[env:seeed_xiao_nrf52840_kit]
extends = nrf52840_base
board = xiao_ble_sense

View File

@@ -10,12 +10,12 @@ build_flags =
-DPIN_SERIAL_TX=PB6
-DPIN_WIRE_SDA=PA15
-DPIN_WIRE_SCL=PB15
-DHAL_DAC_MODULE_ONLY
-DHAL_RNG_MODULE_ENABLED
-DRADIOLIB_EXCLUDE_SX128X=1
-DRADIOLIB_EXCLUDE_SX127X=1
-DRADIOLIB_EXCLUDE_LR11X0=1
-DHAS_SENSOR
-DHAS_SENSOR=1
-DENABLE_HWSERIAL2
-DPIN_SERIAL2_TX=PA2
-DPIN_SERIAL2_RX=PA3
-DHAS_GPS=1
-DGPS_SERIAL_PORT=Serial2
upload_port = stlink

View File

@@ -17,6 +17,8 @@ Do not expect a working Meshtastic device with this target.
#define LED_PIN PB5
#define LED_STATE_ON 1
#define WIO_E5
#if (defined(LED_BUILTIN) && LED_BUILTIN == PNUM_NOT_DEFINED)
#undef LED_BUILTIN
#define LED_BUILTIN (LED_PIN)

View File

@@ -1,4 +1,4 @@
[VERSION]
major = 2
minor = 7
build = 2
build = 3