Compare commits

...

332 Commits

Author SHA1 Message Date
mverch67
4a0a90af16 replace deprecated commands (write_flash) 2025-09-03 09:42:39 +02:00
mverch67
76433f9421 add tlora-pager to device install scripts + fixes 2025-09-03 08:52:10 +02:00
github-actions[bot]
8a8f60d129 Update protobufs (#7831)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-09-02 19:08:05 -05:00
renovate[bot]
b59409bec0 chore(deps): update caveman99-stm32-crypto digest to 1aa30eb (#7808)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-02 18:01:31 -05:00
renovate[bot]
c66125114f chore(deps): update meshtastic/device-ui digest to 8019704 (#7830)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-02 16:17:00 -05:00
renovate[bot]
edb7ec58c6 chore(deps): update platform-native digest to c490bcd (#7814)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-02 11:58:57 -05:00
Ben Meadors
655c6b51fe Try-fix Cardkb detection (#7825)
* Try-fix: CardKB detection regression

* Correct macro
2025-09-02 09:50:15 -05:00
Chloe Bethel
0bd4cefad3 Make ExternalNotification show up in excluded_modules, more STM32 modules (#7797)
* Show ExternalNotification as excluded if it is

* Enable ExternalNotification, SerialModule and RangeTest on STM32WL

* Misc fixes for #7797 - ARCH_STM32 -> ARCH_STM32WL, use less flash by dropping weather station support for serialmodule, set tx/rx pins before begin

* Enable Serial1 on RAK3172, make SerialModule use it (console is on LPUART1)

* Fix SerialModule on RAK3172, fix board definition of RAK3172 to include the right pin mapping.
2025-09-02 07:09:15 -05:00
github-actions[bot]
9b1fb795d7 Upgrade trunk (#7822)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-09-02 06:41:48 -05:00
Tom Fifield
3040e5a7bb Fix GPS that hard code 2080 as the start time. (#7803)
* Fix GPS that hard code 2080 as the start time.

Some GPS chips, such as the AG3335 in T1000e and L96 have a hardcoded
time of 2080-01-05 when they start up.

To fix that in a way that seems permanent, let's ignore times that
are more than 40 years since the firmware was built. We should followup
in late 2039 to see if any changes are needed.

Reported-By: @b8b8

* Update src/gps/RTC.cpp

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

* Put FORTY_YEARS in header and use in both places.

* Restore Ben's nicer log lines.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-02 06:41:26 -05:00
Ben Meadors
3b82d55176 Revert "Add gat562_mesh_tracker_pro device. (#7815)" (#7824)
This reverts commit 7d1300ab66.
2025-09-02 06:17:01 -05:00
Tom Fifield
a6b8202cd4 Hold for 20s after GPS lock (#7801)
* Hold for >20s after GPS lock

GPS chips are designed to stay locked for a while to download some data and save it.
This data is important for speeding up future locks, and making them higher quality.
Our present configuration could make every lock perform similar to first lock.

This patch sets a hold of between 20s and 10% of the lock search time after lock
is acquired. This should allow the GPS to finish its work before we turn it off.

Fixes https://github.com/meshtastic/firmware/issues/7466

* Remove T1000E-specific GPS holds

The new code does the same thing, for all devices.

* Fix publishing settings

* Cleanups, removing unused variables.

* ifdef log line with GPS_DEBUG

* fixQual is not a bool.
2025-09-02 06:06:06 -05:00
Jason P
cfc1bf10c9 If usePreset is False, show value as Custom (#7812) 2025-09-02 06:05:55 -05:00
Wilson
7d1300ab66 Add gat562_mesh_tracker_pro device. (#7815) 2025-09-02 13:06:24 +08:00
Jonathan Bennett
bd3cbfc1ad Add support for the RV-3028 on native Linux (#7802) 2025-09-01 08:04:04 -05:00
github-actions[bot]
fddc4e00ca Automated version bumps (#7790)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-09-01 08:03:03 -05:00
github-actions[bot]
5f7eec5504 Upgrade trunk (#7804)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-09-01 07:58:01 -05:00
Manuel
6b94c297b9 fix: T-LoRa Pager / T-Deck Pro shutdown (#7792)
* power down during LS and shutdown

* fix T-Deck Pro shutdown

* use device specific define

* slightly rephrase the power off display message
2025-09-01 07:57:49 -05:00
Onyx Clawe
edeb25cab5 Update variant.h (#7520)
Updated ADC, Full charge now results in 100% charge being reported instead of 95% charge

Co-authored-by: OnyxtheDragon <58921814+OnyxtheDragon@users.noreply.github.com>
2025-09-01 07:57:15 -05:00
Tom Fifield
44688e8363 Fix device-install.bat baud rate (#7486)
As reported by @gruberaaron , work to improve the 1200bps reset for
esptool caused all runs of device-install.bat to use 1200bps as
the baud rate.

This change removes the general SET "ESPTOOL_BAUD=1200" that was causing
the issues and places the baud settings for reset mode inside the conditional.

Fixes https://github.com/meshtastic/firmware/issues/7172
2025-09-01 14:16:24 +10:00
github-actions[bot]
5ae4ff9162 Upgrade trunk (#7763)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-08-29 13:59:40 -05:00
github-actions[bot]
ed394f5f9d Update protobufs (#7784)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-08-29 13:58:32 -05:00
Tom Fifield
11db6d4dcc Can't trust RTCs to tell the time. (#7779)
Further to https://github.com/meshtastic/firmware/pull/7772 ,
we discovered that some RTCs have hard-coded start times well in the
past.

This patch gives RTCs the same treatment as GPS - if the time is
earlier than BUILD_EPOCH, we don't use it.

Fixes #7771
Fixes #7750
2025-08-29 13:23:14 -05:00
Ben Meadors
4e03df5ea7 Fix freetext hang (#7781)
* Fixed freetext hangs by adding canned modules back to self-sourced packets and transition to SENDING_ACTIVE state

* Update meshmodule handling
2025-08-29 12:09:22 -05:00
Tom Fifield
d3e3a91096 We don't gotTime if time is 2019. (#7772)
There are certain GPS chips that have a hard-coded time in firmware
that they will return before lock. We set our own hard-coded time,
BUILD_EPOCH, that should be newer and use the comparison to not set
a bad time.

In https://github.com/meshtastic/firmware/pull/7261 we introduced
the RTCSetResult and improved it in https://github.com/meshtastic/firmware/pull/7375 .

However, the original try-fix left logic in GPS.cpp that could
still result in broadcasting the bad time.

Further, as part of our fix we cleared the GPS buffer if we didn't
get a good time. The mesh was hurting at the time, so this was a reasonable
approach. However, given time tends to come in when we're trying to get
early lock, this had the potential side effect of throwing away valuable
information to get position lock.

This change reverses the clearBuffer and changes the logic so if time
is not set it will not be broadcast.

Fixes https://github.com/meshtastic/firmware/issues/7771
Fixes https://github.com/meshtastic/firmware/issues/7750
2025-08-29 09:09:13 -05:00
Tom Fifield
b0e8321514 Only send Neighbours if we have some to send. (#7493)
* Only send Neighbours if we have some to send.

The original intent of NeighborInfo was that when a NeighbourInfo
was sent all of the nodes that saw it would reply with NeighbourInfo.
So, NeighbourInfo was sent even if there were no hop-zero nodes in
the NodeDB.

Since 2023, when this was implemented, our understanding of running city-wide
meshes has improved substantially. We have taken steps to reduce the impact
of NeighborInfo over LoRa.

This change aligns with those ideas: we will now only send NeighborInfo
if we have some neighbors to contribute.

The impact of this change is that a node must first see another directly
connected node in another packet type before NeighborInfo is sent. This means
that a node with no neighbors is no longer able to trigger other nodes
to broadcast NeighborInfo. It will, however, receive the regular periodic
broadcast of NeighborInfo, and will be able to send NeighborInfo if it
has at least 1 neighbor.

* Include all the things

* AvOid memleak
2025-08-28 18:45:46 -05:00
Ben Meadors
6c7cff7de2 Merge pull request #7777 from meshtastic/create-pull-request/bump-version
Bump release version
2025-08-28 06:04:37 -05:00
Ben Meadors
834c3c5cc2 Add this back in 2025-08-27 16:24:57 -05:00
Ben Meadors
25a19b49ad This one is not working yet 2025-08-27 15:18:26 -05:00
Ben Meadors
a4d96bebfb Drop for now 2025-08-27 14:35:29 -05:00
renovate[bot]
d21d6d2085 Update meshtastic/device-ui digest to a3e0e1b (#7766)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-27 13:53:34 -05:00
Ben Meadors
26c38ffc8e Remove debug logging 2025-08-27 11:55:27 -05:00
Ben Meadors
237b8908f7 Chainsaw took too much off the top 2025-08-27 09:54:39 -05:00
Ben Meadors
06bccef462 Reinstitute previous streamapi readStream 2025-08-27 07:17:46 -05:00
Ben Meadors
3120bb8fd7 Fix check 2025-08-27 06:50:53 -05:00
Ben Meadors
0903ed8232 Mesh solar integrate (#7764)
* Added HELTEC MeshSolar board. (#7499)

* Added HELTEC MeshSolar board.

* Set emergency shutdown pin as high impedance

* Set emergency shutdown pin as high impedance

Set emergency shutdown pin as high impedance

* Update variants/nrf52840/heltec_mesh_solar/variant.h

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

* Update variants/nrf52840/heltec_mesh_solar/variant.h

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

* Update variants/nrf52840/heltec_mesh_solar/variant.h

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

* Update I2C SCL pin definition in variant.h

---------

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

* Updates

---------

Co-authored-by: Quency-D <55523105+Quency-D@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-27 06:02:54 -05:00
Ben Meadors
f8ba392a24 Add BaseUI support for L1 EInk (#7751)
* Add BaseUI support for L1 EInk

* Fix Eink offset

* Add joystick

* Updates

* Adjust Seeed Wio Tracker L1 E-Ink variant (#7326)

* Rename variant
Needs the -inkhud suffix to work correctly with the web flasher

* Display driver for ZJY122250_0213BAAMFGN

* Remove dead code from nicheGraphics.h
Remnants of T-Echo's nicheGraphics.h file, which was used as a template.

* Use ZJY122250_0213BAAMFGN driver
Improves display health. We don't need as many full refreshes now.

* Tidying

* board_check = true

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>

* Consolidation

* Add hack for existing InkHUD button functionality

---------

Co-authored-by: todd-herbert <herbert.todd@gmail.com>
2025-08-26 20:29:11 -05:00
Ben Meadors
3dd384dd53 Null check 2025-08-26 19:45:26 -05:00
Jonathan Bennett
2c071a3283 Don't use pin 0 on RAK for input (#7755)
* Don't use pin 0 on RAK for input

* Use boolean instead of define

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-08-26 13:41:33 -05:00
Manuel
596cd7e0b6 enable device telemetry (#7757) 2025-08-26 13:39:43 -05:00
Manuel
3f5c30e3b3 T-Lora Pager (#7613)
* initial commit

* preset rotary1 encoder

* define TAB+ESC

* haptic feedback

* allow switch off haptic feedback

* enable audio amplifier

* include PR4684

* fix for tft target

* add ES8311 audio codec

* fix KB scan duplicate

* display workaround to avoid debris

* fix debris on display

* keyboard backlight

* enable screen options

* fsm based bounce-free rotary encoder implementation

* use fsm RotaryEncoder only for T-Lora Pager

* change inputbroker default config to allow using rotary wheel for screens AND menues

---------

Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-08-26 09:35:25 -05:00
github-actions[bot]
1a279c6053 Upgrade trunk (#7677)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-08-26 06:31:38 -05:00
renovate[bot]
3d825c51dd Update meshtastic/device-ui digest to 0f32b64 (#7728)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-24 14:44:51 -05:00
Jonathan Bennett
915f882e1f Pkc fix (#7722) 2025-08-24 10:13:18 -05:00
Lewis He
5136c8ba24 The T-Deck-Pro 4G version sets the modem to be disabled by default. (#7715)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-08-23 06:46:59 -05:00
Ben Meadors
8b42bf7a95 Don't reboot when setting lora config with portduino sim radio (#7716) 2025-08-23 05:47:51 -05:00
Ben Meadors
1daf5aad1f Revert "Add on-screen keyboard implementation on Trackball device. (#7625)" (#7704)
This reverts commit fe3f14a63e.
2025-08-21 06:29:23 -05:00
Wilson
fe3f14a63e Add on-screen keyboard implementation on Trackball device. (#7625)
* Add on-screen keyboard implementation on Wio Tracker L1.

* Update On-Screen Keyboard to new layout.

* The on-screen keyboard dynamically adjusts the key size based on the screen.

* Improve input box display on small screens.

* Optimize the virtual keyboard layout and cursor movement logic, and adjust the keyboard starting position for small and wide screens.

* Optimize the text alignment of numeric keys on ssd1306.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-08-21 18:01:31 +08:00
renovate[bot]
7574bfb7cb Update meshtastic/device-ui digest to 3dc7cf3 (#7698)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-20 14:18:33 -05:00
Jonathan Bennett
ce75bf4496 Initial stab at rak6421 autoconf (#7691)
* Initial stab at rak6421 autoconf

* trunk

* Add crc check to eeprom autoconf

* Trunk again
2025-08-20 14:18:20 -05:00
Jonathan Bennett
5ce47045e7 Add SDL option to BaseUI on Native (#7568)
* Add SDL option to BaseUI on Native

* Update to latest LovyanGFX PR and use LGFX_SDL define

* Move SDL backend to native-sdl target
2025-08-20 12:51:14 -05:00
Austin
57e1725419 Revert "Update platformio/espressif32 to v6.12.0 (#7523)" (#7695)
This reverts commit 11309662a9.
2025-08-20 10:10:39 -04:00
renovate[bot]
11309662a9 Update platformio/espressif32 to v6.12.0 (#7523)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-20 09:08:14 -04:00
github-actions[bot]
890357d579 Update protobufs (#7693)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-08-20 05:53:20 -05:00
Wilson
f413c49555 Add Meshtiny device (#7676)
* Add Meshtiny device - nRF52 OLED upDown encoder

* Update platformio.ini

* Update platformio.ini

* Add GPS Exclude to Meshtiny.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-08-20 11:52:10 +08:00
Ben Meadors
c19f573b49 Fix TLS port bug on default mqtt validation 2025-08-19 20:10:47 -05:00
Jonathan Bennett
5de61b1a3d Only gate PKC behind the simradio CLI flag (#7681)
* Only gate PKC behind the simradio CLI flag

* Hide router.cpp simradio check behind #if ARCH_PORTDUINO
2025-08-19 14:15:05 -05:00
renovate[bot]
1c1462e776 Update meshtastic/device-ui digest to 8f5094b (#7633)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 14:14:12 -05:00
renovate[bot]
eb6ef1cbea Update meshtastic-esp8266-oled-ssd1306 digest to 9573abb (#7686)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 14:13:53 -05:00
renovate[bot]
9654f5b218 Update platform-native digest to 37d9864 (#7684)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 14:13:25 -05:00
Austin
68726a1b0e Docker: fix web assets location (#7683) 2025-08-19 14:06:43 -05:00
Ben Meadors
5b62bbe8e6 Disable for now 2025-08-19 11:30:19 -05:00
jake-b
e55084629a Move heartbeat response before !available guard. (#7672)
* Move heartbeat response before !available guard.

* fix formatting.

---------

Co-authored-by: Jake-B <jake-b@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-08-19 08:10:53 -05:00
Ben Meadors
1691e885f2 Display test results 2025-08-19 06:00:29 -05:00
renovate[bot]
2d7818797d Update platform-native digest to cd32f4e (#7662)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-19 05:43:10 -05:00
github-actions[bot]
f65e2c639e Update protobufs (#7679)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-08-19 05:35:25 -05:00
Jonathan Bennett
95200e8f6b Adds rfswitch on Portduino (#7663)
* Initial attempt to get rfswitch working on Portduino

* Make portduino_config global
2025-08-18 16:33:52 -05:00
github-actions[bot]
36e8dc74f4 Upgrade trunk (#7665)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-08-18 05:52:02 -05:00
Manuel
78c5309e9a apply 180 degree hw roration Indicator BaseUI (#7660) 2025-08-17 14:48:24 -05:00
Thomas Göttgens
9feb1d378e Support for T-Echo Lite, credits to @Szetya for doing all the heavy lifting! (#7636)
* Support for T-Echo Lite, credts to @Szetya for doing all the heavy lifting!
* move define to ini file
2025-08-17 13:37:12 +02:00
Jonathan Bennett
e5e8683cdb Don't update the NodeDB if the nodeinfo has a mismatching public key (#7652) 2025-08-17 05:56:06 -05:00
Jonathan Bennett
d538ad170c Add onboard message for devices with screens (#7655)
* Add onboard message for devices with screens

* Add message for TFT
2025-08-17 05:55:00 -05:00
Jonathan Bennett
c64c196778 Wait for lead up before enable longlong action (#7648) 2025-08-16 06:10:44 -05:00
github-actions[bot]
8e552a9f0c Upgrade trunk (#7626)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-08-16 05:57:20 -05:00
Ben Meadors
a02017a5c8 Remove JSON serialization from most NRF52 targets (#7640)
* Remove JSON serialization from most NRF52 targets

* Slin networking base down for NRF52 by removing syslog

* Update platformio.ini
2025-08-15 19:45:41 -05:00
github-actions[bot]
0046d957f1 Update protobufs (#7647)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-08-15 21:42:51 +02:00
Jonathan Bennett
4a241deb96 Thinknode button and backlight fixes (#7641)
* Thinknode button and backlight fixes

* Save backlight value between reboots
2025-08-15 14:41:21 -05:00
GUVWAF
8d5ae1d5d2 Fix marking LoRa transport mechanism (#7634) 2025-08-15 19:09:25 +02:00
Ben Meadors
e1e89a5e62 Don't include OLED fonts for international character sets by default (#7639) 2025-08-15 09:03:21 -05:00
Ben Meadors
a7be93449e Spacing 2025-08-15 09:00:09 -05:00
Ben Meadors
c8694f9f2d Fix Tracerouter warnings (#7637)
* Static cast to avoid signed comparison

* Another one
2025-08-15 07:03:14 -05:00
Austin
062168cd42 Docker: Update Debian images to trixie (#7621) 2025-08-15 06:19:49 -05:00
Ford Jones
1877a2c531 Prompt user to select destination upon launch of canned message module (#7624)
Co-authored-by: Jason P <applewiz@mac.com>
2025-08-15 05:31:11 -05:00
Austin
52f0e5a3db Fix 'buildroot' target (OpenWRT) (#7620) 2025-08-14 12:31:25 -05:00
github-actions[bot]
ac8c372349 Upgrade trunk to 1.25.0 (#7432)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-08-13 06:26:08 -05:00
github-actions[bot]
1bfa429c38 Automated version bumps (#7614)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-08-12 19:40:35 -05:00
Jonathan Bennett
ddd149945a More spoof remediation (#7612)
* More spoof remediation

* Fix signed comparison error

* Only fire self-bound messages into the routing module

* Update src/mesh/MeshModule.cpp

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

* String const

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-12 16:08:03 -05:00
Constantine
e3dd8164a4 nRF52840 promicro deepsleep fix with some additions (#7407)
* Pro-Micro DeepSleep Quick Fix
It is noticed that some nRF52840 boards (pro-micro in particular)
stopped waking up from the deep sleep state (shutdown state)
with a press of a button.
The problem is in a Serial1.end() call.

* Clear GPREGRET before setting
There are some troubles with that register:
it is recommended to clear it with 0xFF mask
and only after that perform a setting.

* Pro-Micro button SENSE signal
Added SENSE signal on the user button.
It is explicitly enabled now for this platform.

* nRF52 pre-sleep main serial check
Added another usage check for the main Serial.
It could save some nerves in case the port is not in use by any means.
Applied trunk fmt to the file.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-08-12 11:23:34 -05:00
Max
9b8149f14e Adding medium and large RU fonts. Fixing RU string width calculation (#7498)
* Adding  medium and large RU fonts. Fixing string width calculation for RU font

* Update src/graphics/draw/MessageRenderer.cpp

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-08-12 07:22:37 -05:00
renovate[bot]
05f1518951 chore(deps): update actions/download-artifact action to v5 (#7559)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 21:47:21 -05:00
Jonathan Bennett
e26de85b5f Mark meshPackets based on which interface received. (#7589) 2025-08-11 21:47:04 -05:00
renovate[bot]
a2df80e833 chore(deps): update actions/checkout action to v5 (#7605)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 20:58:54 -05:00
Ben Meadors
db238ef524 Log when this happened 2025-08-11 19:49:35 -05:00
Jonathan Bennett
f2b935f48f Stop the bleeding with malicious NodeDB overwrites (#7596) 2025-08-11 15:52:28 -05:00
Thomas Göttgens
e69da71d4e reorder for correct recognition (#7604) 2025-08-11 11:53:01 +02:00
Ben Meadors
7505fe7a7c Update device-ui deps 2025-08-09 10:38:09 -05:00
Ben Meadors
f6857f1bcb Heartbeat has a nonce now 2025-08-09 10:17:08 -05:00
github-actions[bot]
7fe2c74139 Update protobufs (#7588)
Co-authored-by: jp-bennett <5630967+jp-bennett@users.noreply.github.com>
2025-08-09 09:14:22 -05:00
github-actions[bot]
be60f9612e Update protobufs (#7587)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-08-09 08:14:04 -05:00
github-actions[bot]
2de9f015b1 Automated version bumps (#7586)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-08-09 08:12:24 -05:00
Ben Meadors
c1f4f79d4a Revert "128row display (#7511)"
This reverts commit d1f3c3c982.
2025-08-09 06:11:56 -05:00
renovate[bot]
7b874cf597 chore(deps): update meshtastic/device-ui digest to d044c01 (#7578)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-08 11:31:49 -05:00
Jonathan Bennett
8568b56ac6 Fix a crash on Native reboot (#7570) 2025-08-07 12:28:01 -05:00
renovate[bot]
f2a880f813 chore(deps): update adafruit shtc3 to v1.0.2 (#7557)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-07 13:56:17 +02:00
Jonathan Bennett
691327b2db Initial support for the ThinkNode M5 (#7502)
* Initial support for the ThinkNode M5

* Update variants/esp32s3/ELECROW-ThinkNode-M5/platformio.ini

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

* Cleanup variant.h for Elecrow Thinknode M5

* Properly detect battery voltage

* Turn backlight off when screen sleeps

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Austin <vidplace7@gmail.com>
2025-08-07 06:28:15 -05:00
oscgonfer
a23c58c10a Avoid acquiring lock twice (#7555)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-08-06 06:38:36 -05:00
Jonathan Bennett
27c6b24e3a Rather than mysteriously rebooting, regenerate the keys and infrom the user. (#7558) 2025-08-05 19:53:25 -05:00
mrab
384436e937 fix: ina226 was not calibrated during init (#7547)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-08-05 06:34:52 -05:00
Jason P
eb30aae486 Create better log message for users (#7548) 2025-08-04 16:32:27 -05:00
Jason P
079286da04 Only toggle screen wake, don't break banners (#7545)
* Only toggle screen wake, don't break banners
* Fix code - only needed a small line change
2025-08-04 19:33:45 +02:00
tg-mw
0130899b3b Fix Melopero RV3028 RTC Settings (#7524) 2025-08-04 18:42:39 +02:00
Thomas Göttgens
d1f3c3c982 128row display (#7511)
* Fix 128 row monochrome display
* trunk fmt
* fix assignment
2025-08-04 17:25:31 +02:00
Jacob Powers
3b6eefa8bb feat: event mode - limit smart position updates to at most every 5m (#7505)
* feat: event mode - limit smart position updates to at most every 5m

* fix: convert 600 to 600000ms for 5min threshold

* fix: correct 5min threshold to 300000ms

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-31 21:45:34 -05:00
HarukiToreda
5107531425 Remember destination fix (#7427)
* T-watch screen misalignment fix

* Trunk fix

* Rember Last Receipient Node or channel

When a new freetext or preset message is sent and a destination is selected, the next message would forget the previously selected destination and would need to be selected again. With this fix it will remember the last destination selected until changed again.

* Fix for reply function to remember last messaged

* trunk check

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Jason P <applewiz@mac.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-31 07:36:03 -05:00
Ben Meadors
88655ffc44 Move BLE toggle menu option and add confirmation for canned messages in L1 (#7516)
* Move bluetooth to system menu and add confirmation for canned messages

* Cruft

* Handle else

* Warn

* Fixed screen reset
2025-07-31 07:34:41 -05:00
mikecarper
10bd10b9d1 bugfix Syntax error: "(" unexpected in device-update.sh (#7514)
* Update device-update.sh to use /bin/bash

* Update meshtasticd.postinst to use /bin/bash

* Update meshtasticd.postrm to use /bin/bash
2025-07-31 06:02:09 -05:00
renovate[bot]
956a0f102b Update platformio/ststm32 to v19.3.0 (#7512)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-31 06:00:58 -05:00
Tom Fifield
bdedd0e1fe Airoha GPS - ignore estimated fixes (#7429)
TinyGPS Fix Quality has this information:
				0 - fix not available,
				1 - GPS fix,
				2 - Differential GPS fix (values above 2 are 2.3 features)
				3 = PPS fix
				4 = Real Time Kinematic
				5 = Float RTK
				6 = estimated (dead reckoning)
				7 = Manual input mode
				8 = Simulation mode

the previous Airoha code would allow quality >0 , which includes
estimated positions. These wouldn't be passed through to the mesh
due to other checks, but would affect the Airoha GPS_FIX_HOLD_TIME
calculations.

Changes the calculation to 1 >= quality <=5 .
2025-07-31 12:21:10 +10:00
Tom Fifield
4c901033b2 Workaround Webserver needing to stay up while Wifi is turned off (#7484)
Expertly triaged by @philon- , turning off wifi using the HTTP API
did not work. That was because we only served the HTTP API if Wifi
was deemed to be available, but mid-way through turning it off Wifi
was still available, but the configuration we were checking said it wasn't.

This patch introduces an additional way the system can determine if Wifi
is available, by referring to the WiFi.status(). This means that in that
limbo state where Wifi has been set to be turned off, but the configuration
has not been saved and it is still up, the HTTP API will stay up long enough
to save the configuration.

Fixes https://github.com/meshtastic/firmware/issues/6965
2025-07-30 07:47:00 -05:00
Ben Meadors
7d926da98c Heartbeat response (#7506)
* Heartbeat response

* Move it

* Add debug log for visibility
2025-07-30 07:40:27 -05:00
github-actions[bot]
1b793d1f23 Update protobufs (#7508)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-07-30 06:29:03 -05:00
Chloe Bethel
b5a8e8f51b DEBUG_MUTE correctness (#7492)
* treewide: make 'ifdef DEBUG_PORT' guards also take into account DEBUG_MUTE

* stm32wl: Add a guard against having debug prints turned on without PIO_FRAMEWORK_ARDUINO_NANOLIB_FLOAT_PRINTF defined

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-29 08:51:38 +10:00
Ben Meadors
cc5d00e211 Core portnums rebroadcast mode whitelist instead of blacklist (#7487) 2025-07-28 12:37:37 -05:00
Ben Meadors
1a8ab2aadc NodeDB count on MyNodeInfo for client progress reporting (#7489) 2025-07-28 12:23:59 -05:00
Thomas Göttgens
608fdc6f52 Santa may be checking his list twice, but we only need this in the platformio.ini (#7490) 2025-07-28 09:47:46 -05:00
rradillen
1d8638b47d [7353] Add all telemetry fields to json output (#7363)
* Serializer bugfix

* Remove duplicate test

* fix tests

* fix float precision issues

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-28 09:23:04 -05:00
Ben Meadors
3ecff48722 Set firmware edition (for events) from userprefs (#7488)
* Set firmware edition (for events) from userprefs

* Spaces in the right places
2025-07-28 07:31:33 -05:00
mikecarper
aa3b14ce72 bugfix Add rssi and snr to the store and forward code. (#7462)
* Update StoreForwardModule.cpp

* Update StoreForwardModule.h

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-27 20:03:01 -05:00
Ben Meadors
28aeb0f09e Validate Serial config console override modes (#7470)
* Validate serial config console override modes

* Update src/modules/SerialModule.cpp

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

* Disable

* Guard serial module

* Guards

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-26 19:55:54 -05:00
github-actions[bot]
7c5e2c5393 Update protobufs (#7473)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-07-26 16:21:49 -05:00
Tom Fifield
df8b629c2c Clear position on GPS deactivation, unless using fixed position (#7464)
* Clear position on GPS deactivation, unless using fixed position

As reported by @dreimal8 , and confirmed by @tuxmobil , when using
and then subsequently disabling GPS the last position retrieved from
the GPS was stored and continued to be broadcast.

This change introduces a check to see if we are transitioning from
GPS Enabled to GPS Disabled or Not Present. If we are, and fixed
position is not in use, then we clear the local position.

This will prevent inaccurate and undesired position broadcasts for those
who disable their GPS.

Fixes https://github.com/meshtastic/firmware/issues/7228

* Update triple click to also clear position

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-26 08:09:26 -05:00
Ben Meadors
a506dc6b65 Fix MQTT config bugs (#7446)
* Fix mqtt config bugs

* Apply suggestion from @Copilot

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

* Add client notification

* Verbiage

* Update src/mqtt/MQTT.cpp

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

* Update src/mqtt/MQTT.cpp

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

* Update src/mqtt/MQTT.cpp

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

* Update src/mqtt/MQTT.cpp

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

* Remove test that is no longer true

* This test no longer exists

* Fix client notification crap

* Suppress false positive

* Revert "Suppress false positive"

This reverts commit bead96eaee.

* Try macro exclusion

* Derp

* Fix

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jason P <applewiz@mac.com>
2025-07-26 05:38:24 -05:00
Iris
fc1e6ccb8c Rename Platformio.ini to platformio.ini (#7468) 2025-07-26 05:13:02 -05:00
Iris
bbc638ab82 Create Platformio.ini (#7450) 2025-07-25 15:36:37 -05:00
Austin
4f57a2e248 Build RP2350 (Pi Pico 2) (#7441) 2025-07-25 22:25:50 +02:00
Jason P
4c6db2c5bd Fix MHz label (#7455) 2025-07-25 08:10:35 -05:00
Pedro
bbe548bc98 Add BRT3 timezone option to TZPicker menu (#7438) 2025-07-24 22:42:42 -05:00
Pedro
d1fbf65c5d Fix timezone definition for UTC in TZPicker function (#7442) 2025-07-24 21:57:40 -05:00
Wilson
7a4a915312 Add Trace Route on BaseUI (#7386)
* Add TraceRoute function to menus and modules to support node path tracing

* Adjust text spacing and line wrapping logic in trace route result result.

* Add HAS_SCREEN for TraceRouteModule drawFrame.

---------

Co-authored-by: Tom Fifield <tom@tomfifield.net>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-24 17:23:45 -05:00
Austin
4f895f744b Take control of our PRs! (#7445) 2025-07-24 06:13:23 -05:00
Austin
66a831dfa8 Actions: Combine embedded builds // split by variant subdir (#7417) 2025-07-23 13:41:08 -04:00
Pedro
516597a73e Add NP_865 and BR_902 to device menu (#7434) 2025-07-23 14:56:22 +02:00
Tom Fifield
4eb6c9fb8e Add BR_902, Brazil 902MHz-907.5MHz (#7399)
As reported by @barbabarros , the Brazilian government has specific support for LoRA[1] across multiple frequencies[2][3].
We currently support Brazil through the ANZ/AU915 band. However, Brazil also has another frequency available for use:
902 - 907.5 MHz , 1W power limit, no duty cycle restrictions

[1]  https://sistemas.anatel.gov.br/anexar-api/publico/anexos/download/a028ab5cc4e3f97442830bba0c8bd1dd 
[2] 
https://informacoes.anatel.gov.br/legislacao/resolucoes/2025/2001-resolucao-772 
[3] https://informacoes.anatel.gov.br/legislacao/atos-de-certificacao-de-produtos/2017/1139-ato-14448#item10

Protobuf patch: https://github.com/meshtastic/protobufs/pull/737

Fixes https://github.com/meshtastic/firmware/issues/3741

Co-authored-by: Austin <vidplace7@gmail.com>
2025-07-23 14:55:17 +02:00
saiman pokhrel
46e2ae8860 Add Nepal 865 MHz to 868 MHz (#7380)
Co-authored-by: Tom Fifield <tom@tomfifield.net>
Co-authored-by: Austin <vidplace7@gmail.com>
2025-07-23 14:54:43 +02:00
renovate[bot]
54c0cbeb66 Update meshtastic/device-ui digest to c75d545 (#7435)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-23 07:46:34 -05:00
Jonathan Bennett
82ddf4732a Deprecate disable_triple_click config (#7425) 2025-07-23 05:57:17 -05:00
Austin
ed0cdefb44 Use platformio-core to build the matrix (#7424)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-22 21:01:29 -05:00
Tom Fifield
8836be0f47 AG3335 GPS: Use NAVIC in India/Nepal, L1+L5 elsewhere. (#7413)
As determined by @b8b8 , enabling NAVIC meant the more modern L5 GPS
signal was not used (L1 GPS is always available).

NAVIC, India's GNSS, probably provides the best coverage in India and
the neighbouring region. However, outside of NAVIC's coverage area, L5
GPS is highly desirable.

This patch amends the AG3335-family GPS configuration to enable L5 GPS
coverage by default. If the Lora region is set to India or Nepal,
NAVIC will be enabled instead.
2025-07-22 21:00:34 -05:00
github-actions[bot]
96f63f3945 Update protobufs (#7422)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-07-22 13:49:53 -05:00
Jonathan Bennett
d80dcd6afd Fix InkHUD shutdown code 2025-07-22 08:49:33 -05:00
Jonathan Bennett
2087629a47 Add a verbose mode flag to meshtasticd (#7416) 2025-07-22 06:22:23 -05:00
github-actions[bot]
878d68c5ef Upgrade trunk (#7420)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-07-22 06:22:07 -05:00
Jonathan Bennett
86960cdb1d Moves the shutdown thread into the Power class, make shutdown and reboot private (#7415) 2025-07-22 06:21:51 -05:00
Jonathan Bennett
fff12979a2 Set canned_message.enabled to true when setting defaults (#7414)
* Set canned_message.enabled to true when setting defaults

* Re-split canned messages on update
2025-07-21 19:31:07 -05:00
Austin
6c12baf4ed Migrate remaining variants to new dir structure (#7412) 2025-07-21 19:28:14 -05:00
whywilson
29449a71d4 When outputting RTTTL ringtones, you can still hear a periodic buzzing sound. This problem is fixed in this commit. 2025-07-21 14:44:41 -05:00
Austin
9b983b6487 nRF52840: Migrate variants to new structure (#7396) 2025-07-21 14:13:02 -05:00
Austin
806bfa54b5 Renovate: Use github-tags for XPowersLib updates (#7411) 2025-07-21 14:04:46 -05:00
Austin
920aeeeba5 Actions: pull_request_target is fun (#7398) 2025-07-21 14:03:13 -05:00
github-actions[bot]
32418448de Update protobufs (#7410)
Co-authored-by: caveman99 <25002+caveman99@users.noreply.github.com>
2025-07-21 20:32:55 +02:00
Manuel
b3525c2569 T-Deck Pro support (#6936)
* initial draft

* fix touchscreen

* fix touchscreen

* optimize GPS

* battery management

* cleanup comments

* enable vibration motor

* refactored TCA8418Keyboard

* update HW_VENDOR id

* manual fixes after merge

* fix keyboard/BQ27220 detection

* add BQ27220

* modify charge voltage and current

* update XpowerLib

* design capacity

* try-fix charge behavior

* improve Vbus detection

* moved variant into esp32s3 folder

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
2025-07-21 19:33:24 +02:00
github-actions[bot]
19dc2873c5 Upgrade trunk (#7400)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-07-21 20:07:45 +10:00
Austin
25b8d9b0ca ARCH_STM32*WL* macro fix (#7397) 2025-07-20 23:30:52 -04:00
Ben Meadors
8aef3c44f4 Text message rate limiting should return routing error instead (#7365)
* Text message rate limiting should return routing error instead

* Proper rooting

* Update PhoneAPI.cpp

* Update PhoneAPI.cpp
2025-07-20 20:12:10 -05:00
Ben Meadors
8345c21eff STM32 doesn't play 2025-07-20 20:02:32 -05:00
Jonathan Bennett
36b94cf823 Unify the shutdown proceedure (#7393)
* Unify the shutdown proceedure

* Don't double save nodeDB on shutdown

* Re-tool button shutdown to better correspond to tones

* Beep then save

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-20 18:53:40 -05:00
Austin
475cfe4af2 ESP32s3: Migrate variants to new structure (#7343) 2025-07-20 16:47:37 -05:00
Thomas Göttgens
b851b15a73 fix UDP builds on nRF (#7394)
* fix UDP builds on nRF
* fix rp2040 too
2025-07-20 23:13:50 +02:00
github-actions[bot]
73347c2542 Update protobufs (#7395)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-07-20 15:03:56 -05:00
Jason P
bc9023399d Restore High Resolution Hour Hand (#7392)
* Restore High Resolution Hour Hand

* Drop the int16_t
2025-07-20 13:43:54 -05:00
Thomas Göttgens
a9c9b96eb6 UDP for RAK4631 Eth Gw and the t-eth-elite. Solves #7149 (#7385)
* UDP for RAK4631 Eth Gw and the t-eth-elite. Also enable IP output on Portduino. Solves #7149

* Copilot suggestion

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

* fix portduino build

* initialize local port

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-20 06:22:00 -05:00
Austin
1c2a3c620f STM32: Migrate variants to new structure (#7389) 2025-07-20 06:21:17 -05:00
Austin
9313d04726 RP2040/RP2350: Migrate variants to new structure (#7345) 2025-07-20 06:20:57 -05:00
github-actions[bot]
44518fea14 Upgrade trunk (#7349)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-07-20 06:20:07 -05:00
Jonathan Bennett
91049d0db3 Misc cppcheck fixes (#7370) 2025-07-20 06:19:45 -05:00
Austin
855514b4f3 ESP32c3: Migrate variants to new structure (#7342) 2025-07-19 19:55:33 -05:00
Austin
974741a366 ESP32: Initial sort variants by platform (#7340) 2025-07-19 18:41:59 -05:00
Austin
5d98f7e307 Actions: Enforce PR labels (#7379) 2025-07-19 11:38:05 -05:00
github-actions[bot]
3ca45ae99c automated bumps (#7383)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-07-19 10:41:01 -05:00
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
Jonathan Bennett
5f5698ccc0 Explicitly include meshUtils.h 2025-07-10 10:29:33 -05:00
Jonathan Bennett
74c735d5fb Gate screen code behind IF_SCREEN() 2025-07-10 10:20:44 -05:00
Jonathan Bennett
107dec22bd Remove bogus validation check 2025-07-10 10:12:02 -05:00
Jonathan Bennett
0795b21c2b Key verification flow on BaseUI (#7240) 2025-07-10 09:45:36 -05:00
renovate[bot]
a7e516d6f6 Update Adafruit INA260 to v1.5.3 (#7270)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-09 15:11:05 +08:00
Austin
f6d378255c Actions: Re-Add nrf52 hex release (rak4631) (#7276) 2025-07-08 23:53:51 -04:00
Austin
19d831d20d Whoops! Re-Add nRF52 OTA zips (#7275) 2025-07-08 21:33:59 -04:00
Austin
00495140bd GitHub Actions faster!! (#7268)
Use new meshtastic/gh-action-firmware Action

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-08 15:14:05 -05:00
Ben Meadors
354f149338 Make PacketHistory logging less chatty (#7272) 2025-07-08 15:12:44 -05:00
Jason P
999e1207a5 Show user which option is currently elected (#7271) 2025-07-08 14:38:38 -05:00
Jason P
916587c2a6 Update Bluetooth Toggle to match other variants (#7269) 2025-07-08 13:38:07 -05:00
todd-herbert
db4e4e6e53 Heltec Wireless Paper, VM-E213 Hardware Revisions (#7258)
* Tests to identify display model

* (InkHUD) SSD1682 controller IC
Has a few quirks, gets its own base class

* (InkHUD) E0213A367 Display
For Heltec Wireless Paper V1.1.1, V1.2
For Heltec VM-E213 V1.1

* (InkHUD) Select display model at boot

* (BaseUI) Wrapper to combine multiple GxEPD2 drivers
Workaround for issue of GxEPD2_BW objects not having a shared base class. Allows us to select a driver at runtime.
https://github.com/meshtastic/firmware/issues/6851#issuecomment-2905353447

* (BaseUI) Select E-Ink model at boot

* (InkHUD) SSD1682 deep sleep

* (InkHUD) No deep sleep for SSD1682

* (InkHUD) Fully no-op deep sleep for SSD1682
2025-07-08 13:01:48 -05:00
Jason P
9c08220d24 TFT_MESH Fixes Across Various Devices (#7247)
* Rename "r,g,b" variables to having a TFT_MESH_ prefix

* Reboot and then user options

* Restore TFT_MESH on any ST7789Spi driver
2025-07-08 06:24:12 -05:00
HarukiToreda
88b299dd41 Modules and favorite screen fix (#7264)
* T-watch screen misalignment fix

* Trunk fix

* Fix for favorite frame when module screen is enabled
2025-07-08 06:22:57 -05:00
github-actions[bot]
19af2d9e3b Upgrade trunk (#7266)
Co-authored-by: sachaw <11172820+sachaw@users.noreply.github.com>
2025-07-08 06:22:24 -05:00
Jonathan Bennett
fa23be4424 Revert "GitHub Actions faster!! (#7244)" (#7262)
This reverts commit f2fb473ecf.
2025-07-07 19:50:44 -05:00
Ing. Jan Kaláb
e1f40c2db9 Fix install script (#7259)
This partially reverse 2ab717c (#7143), fixing the install script. It looks like a bad/missed copy/paste.
2025-07-07 19:36:21 -05:00
Ben Meadors
415dc4aa47 Try-fix: L76K spamming bad times can crash nodes (#7261)
* Try-fix: Clear GPS buffer when we encounter a bad time in NMEA

* Fix signed int warnings
2025-07-07 19:35:57 -05:00
Austin
f2fb473ecf GitHub Actions faster!! (#7244)
Use new meshtastic/gh-action-firmware Action

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-07 19:34:25 -05:00
Max
f95c77b8bd Fast fix, remove saving tx power inside limitPower() (#7255) 2025-07-07 07:50:13 -05:00
github-actions[bot]
09d4ee1ea7 Upgrade trunk (#7254)
Co-authored-by: sachaw <11172820+sachaw@users.noreply.github.com>
2025-07-07 06:37:15 -05:00
Jonathan Bennett
40c586ca97 Automatically bail user out of displaymode_color when not HAS_TFT (#7248) 2025-07-06 16:36:22 -05:00
renovate[bot]
708978911b chore(deps): update meshtastic/device-ui digest to 8c7092c (#7238)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-05 19:26:20 -05:00
Jonathan Bennett
2a26209889 Trunk 2025-07-05 12:56:29 -05:00
Aiden Fox Ivey
1378657206 Fix documentation comments. 2025-07-05 12:50:33 -05:00
Aiden Fox Ivey
98d010761e Add constant time compare to AES-CCM
Signed-off-by: Aiden Fox Ivey <aiden@aidenfoxivey.com>
2025-07-05 12:50:24 -05:00
Ben Meadors
798b1f4d86 Add HWIDs for T1000-E in DFU mode (#7235) 2025-07-05 07:35:20 -05:00
Jonathan Bennett
29893e0c28 Don't run ble getFromRadio() unless the phone has requested a packet (#7231) 2025-07-04 14:22:59 -05:00
github-actions[bot]
1994bb3cd1 automated bumps (#7227)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-07-04 08:10:23 -05:00
Jonathan Bennett
f35ca812a3 Add a WiFi menu that can toggle back to Bluetooth (#7226)
* Add Kazakhstan to the BaseUI LoRa chooser

* Add a WiFi menu that can toggle back to Bluetooth
2025-07-04 05:30:56 -05:00
renovate[bot]
abbeb4874d chore(deps): update xpowerslib to v0.3.0 (#7210)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-04 13:12:24 +08:00
Jonathan Bennett
0f96bd7a26 Add Kazakhstan to the BaseUI LoRa chooser (#7224) 2025-07-04 10:31:09 +08:00
renovate[bot]
dfb07e8bd2 chore(deps): update meshtastic-esp32_https_server digest to 3223704 (#7225) 2025-07-03 20:16:53 -05:00
Matt Smith
ff4eed08bc Fixed --change-mode option since it was broken (#7144)
getopts can't parse double-dash options so it had to be done separately.  Also fixed where CHANGE_MODE was checked since it wasn't working either.
2025-07-03 19:41:17 -05:00
Tom Fifield
f13dc5b903 Add Kazakhstan frequencies (#7209)
As reported by @KZ1R , Kazakhstan has frequencies in use for Lora devices that are not covered by our existing band selections.

This adds
* KZ_433 433.075 - 434.775 MHz <10 mW EIRP, Low Powered Devices (LPD)
* KZ_863 863 - 868 MHz <25 mW EIRP, 500kHz channels allowed, must not be used at airfields

Legal ref provided in https://github.com/meshtastic/firmware/issues/7204 and verified.

https://www.gov.kz/memleket/entities/mdai/press/article/details/6128
 Order of the Ministry of Investments and Development of the Republic of Kazakhstan No. 34 dated January 21, 2015.
 Published on 01 July 2024 19:03 Updated on 01 July 2024

Fixes https://github.com/meshtastic/firmware/issues/7204
2025-07-03 18:34:46 -05:00
Jonathan Bennett
c1431f4f9a Disable low brightness, as this soft-bricks at least the L1 (#7223) 2025-07-03 18:34:04 -05:00
Jason P
93132fad28 Battery Layout Updates and Icons Changes (#7221)
* Testing battery states with some info lines in the drawCommonHeader

* Update logic of USB connected, update images for states

* Tweak battery layout for isHighResolution

* Hide the magic 101%

* Adjust padding for SENSECAP_INDICATOR

* Excessive logs are unnecessary as troubleshooting is done.

* Reduce excess code - simplify readability

* Restore Lightning Bolt for Charging and related alignment issues

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-07-03 17:20:51 -05:00
todd-herbert
2254d551f4 Honor custom userPrefs boot-screens in InkHUD (#7217)
* Honor custom boot screen from userPrefs.jsonc

* Meshtastic logo when powered off, userPrefs logo at boot

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-03 15:40:43 -05:00
Austin
f2d3f54824 No routers allowed! (#7220) 2025-07-03 13:10:35 -07:00
github-actions[bot]
6fa597bc5d [create-pull-request] automated change (#7216)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-07-03 08:17:42 -05:00
Jonathan Bennett
b02e58521d Add GPIO edge for Native Trackball/Joystick (#7212)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-03 06:53:27 -05:00
Jonathan Bennett
81828c6244 Don't set non-existent pin on e290 (#7213)
* Don't set non-existent pin on e290

* Don't twiddle imaginary pins on the e213, either
2025-07-03 14:52:55 +10:00
Jonathan Bennett
549250b91a Good bot -- make array large enough to handle all the possible options 2025-07-02 22:39:29 -05:00
Ben Meadors
409dfe22ae Fix Seeed L1 board to enable consistent PIO flashing (#7211) 2025-07-02 20:58:15 -05:00
Jonathan Bennett
a6be2e46ed 2.7 fixes w2 (#7148)
* Initial work on splitting notification renderer into components for reuse

* More progress

* Fix notification popup

* more fix, less crash

* Adjustments for OLED on keeping menus tidy, added Bluetooth Toggle to Home frame. Also widen the frame slightly if you have a scroll bar

* Small changes for EInk to not crowd elements

* Change System frame menu over to better match actions; added color picker for T114

* Fix build errors and add T190 for testing

* Logic gates are hard sometimes

* Screen Color Picker changes, defined Yellow as a Color.

* Additional colors and tuning

* Abandon std::sort in NodeDB, and associated fixes (#7175)

* Generate short name for nodes that don't have user yet

* Add reboot menu

* Sort fixes

* noop sort option to avoid infinite loop

* Refactor Overlay Banner

* Continuing work on Color Picker

* Add BaseUI menus to add and remove Favorited Nodes

* Create TFT_MESH_OVERRIDE for variants.h and defined colors

* Trigger a NodeStatus update at the end of setup() to get fresh data on display at boot.

* T114 defaults to White, Yellow is now bright Yellow

* Revert "T114 defaults to White, Yellow is now bright Yellow"

This reverts commit 8d05e17f11.

* Only show OEM text if not OLED

* Adjust OEM logo to maximize visible area

* Start plumbing in Color Picker changes

* Finished plumbing

* Fix warning

* Revert "Fix warning"

This reverts commit 2e8aecd52d.

* Fix display not fully redrawing

* T-Deck should get color too

* Emote Revamp

* Update emotes.cpp

* Poo Emote fix

* Trunk fix

* Add secret test menu and number picker

* Missed bits

* Save colors between reboots

* Save Clock Face election to protobuf

* Make reboot first, then settings

* Add padding for single line pop-ups

* Compass saving and faster menus

* Resolve build issue with Excluding GPS

* Resolve issue with memory bars on EInk

* Add brightness settings for supported screen (#7182)

* Add brightness menu.

* add loop destination selection.

* Bring back color (and sanity) to the menus!

* Trunk

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jason P <applewiz@mac.com>
Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
Co-authored-by: Wilson <m.tools@qq.com>
2025-07-02 20:50:49 -05:00
renovate[bot]
3fdefe8289 chore(deps): update sensirion i2c scd4x to v1.1.0 (#7207)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-03 10:22:14 +10:00
todd-herbert
1f85e2a02a Additional larger font for InkHUD UI (#7201)
* Add 12pt fonts

* Add fontMedium
In addition to fontSmall and fontLarge

* Set fonts in nicheGraphics.h

* Change all uses of fontLarge to fontMedium
fontLarge was previously set at 9pt. fontLarge is now 12pt, fontMedium is 9pt. (NB: fonts may be customized per-variant)

* Use fontLarge with "All Messages" and "DMs" applets

* Documentation
2025-07-02 19:18:34 -05:00
Austin
f99ac2104c Add customizable boot logo based on resolution (#7146) 2025-07-02 11:53:12 -07:00
Austin
553fc0cb1b Renovate comment for sensirion/Sensirion I2C SCD4x (#7202) 2025-07-02 09:11:39 -05:00
Austin
f39c7ad47e mDNS: Remove HTTP/HTTPS. Advertise shortname. (#7162) 2025-07-02 07:20:40 -05:00
Razurac
e505ec847e Added option to invert screen on InkHUD (#7075)
* Added option to invert screen on InkHUD

* Rewrite to make use of existing config.display.displaymode

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-02 06:06:02 -05:00
Daniel.Cao
90e99b2bac feat: add support for RAK3312 (New RAKwireless wiscore ESP32-S3 + SX1262) (#7115)
* Add support for RAK3112 variant with necessary configurations and definitions

* Add the configuration of the LED pin

* Refactor rak3112 variant configuration: update name, remove unused files,

* Update RAK3112 configuration: refine memory settings,  adjust GPS pin definitions

* Update RAK3112 hardware vendor definition to use correct model

* configure LED pins and update module definitions

* Update USB mode configuration for RAK3112 board

* Update power and battery configuration in rak3112 variant

* Cancel the modification of mesh.pb.h

* Rename RAKwireless RAK3112 to RAK3312

---------

Co-authored-by: daniel <daniel.cao@rakwireless.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-02 06:03:42 -05:00
github-actions[bot]
d25240b33b [create-pull-request] automated change (#7199)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-07-02 06:03:00 -05:00
Chloe Bethel
d494c23a88 Enable telemetry and I2C sensors on STM32WL (except accelerometers) (#7008)
* Update platformio inis for stm32 platform and wio-e5 variant for enabling i2c

* Don't reference timezone functions if MESHTASTIC_EXCLUDE_TZ is defined

* Use custom pow_of_two in RadioInterface instead of floating-point pow()

* First pass: enable sensors for STM32wL

* Fix AirQualityTelemetryModule being created if the PM25AQI header is missing

* Link in power sensor libraries

* more ini tweaks

* Add =1 to EXCLUDE defines, fix indentation.

* Drop HAS_WIRE in ini, it's defined in architecture.h

* Fix build when power sensor libraries are missing

Make MAX sensor integration into Power.cpp optional based on its library header existing.
Also make NullSensor expose a voltage and current sensor, because Power calls directly into these for INA sensors.
This lets us remove all the deps for the STM32WL platform.

* Change default I2C for RAK3172 to be I2C1, not I2C2

* Respect the laws of mathematics (oops)
2025-07-02 06:01:45 -05:00
Mictronics
17f8303e01 Fix build when MESHTASTIC_EXCLUDE_GPS is defined (#7154)
* 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.

---------

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-02 05:59:43 -05:00
dylanli
aadea89202 fix bug of cant't switch between two applets side-by-side (#7195) 2025-07-02 05:49:47 -05:00
github-actions[bot]
53013e9a7e Upgrade trunk (#7151)
Co-authored-by: sachaw <11172820+sachaw@users.noreply.github.com>
2025-07-02 18:34:51 +10:00
Tymoteusz Jankowski
30eec01f55 Fix hydra radio (#7192)
Added missing defines to variant.h
2025-07-02 13:41:56 +10:00
dylanli
cc961d7762 update seeed device battery level map (#7194) 2025-07-02 13:39:51 +10:00
github-actions[bot]
a7528d777a [create-pull-request] automated change (#7193)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-07-01 20:20:22 -05:00
todd-herbert
13013a272f Limited emoji support for InkHUD (#7176)
* Cram a few emoji into AdafruitGFX fonts
Values which would normally be assigned to unprintable control characters

* Another sneaky string which may contain UTF-8 chars

* Document emoji

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-01 20:18:14 -05:00
Max
3ea96bb6e1 Log TX power after limits applyng and store it in config (#7065)
* Log and save in config lora tx_power after limits applyng

* Log and save in config lora tx_power after limits applyng

* Trunk fmt

* Remove duplicate logic

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-07-01 08:47:42 -05:00
Tom Fifield
baf0e9c7e6 Add detection framework for multiple AirQuality sensors (#7187)
* Add detection framework for multiple AirQuality sensors

Now we have the ability to detect multiple AirQualitySensors,
follow the lead of other sensor types and create supporting methods
and objects for using this information.

Continued cherry-picking to get #4601 over the line :)

Co-Authored-By: @Coloradohusky

* Update src/main.cpp

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-01 21:27:44 +10:00
dylanli
598eebfb10 fix t1000-e battery level map (#7186) 2025-07-01 20:16:48 +10:00
Tom Fifield
5841c889ba Add detection code for SCD4X (#7185)
* Add detection code for SCD4X

This patch adds I2C detection support SCD40/SDC41 CO2 sensors.

It's a start to get #4601 over the line :)

Co-Authored-By: @Coloradohusky

* Remove SCD4X from Portduino
2025-07-01 19:34:03 +10:00
renovate[bot]
4bd416413a chore(deps): update meshtastic/device-ui digest to 4b7bf36 (#7178)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 16:04:12 -05:00
github-actions[bot]
be06a7d881 automated bumps (#7155)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-06-30 06:05:43 -05:00
Andrew Yong
26df4f8142 fix(xiao_ble): Define xiao_ble I2C pins in parent variant (fixes #7163) (#7164)
This restores the previously-defined I2C pins that got lost in the cleanup (#7024)

Signed-off-by: Andrew Yong <me@ndoo.sg>
2025-06-30 06:05:24 -05:00
Jonathan Bennett
b6a13f1114 Add check for theoretically impossible comparison, and drop nodenum comparison (#7165) 2025-06-28 22:54:03 -05:00
Jonathan Bennett
2bcf608654 Last second fixes (#7156)
* Ditch the 30 second delay for button presses

* Only order strictly weakly

* Too many comments!

* Only sort the populated meshNodes
2025-06-28 08:19:31 -05:00
Jonathan Bennett
705515ace2 Resize meshNodes to MAX + 1 to avoid crash during sort 2025-06-27 11:46:33 -05:00
Jonathan Bennett
a97df4bb52 Sanity check incoming UDP 2025-06-27 11:22:16 -05:00
porkcube
f6743798e2 cleanup Shutting down -> Shutting Down awkwardness (#7099)
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-06-27 11:09:04 -05:00
Ben Meadors
2ea70927c8 Revert "automated bumps (#7097)"
This reverts commit 4308bbc156.
2025-06-27 11:07:58 -05:00
Jonathan Bennett
de5b55921e Extra check on UDP packets 2025-06-27 11:06:19 -05:00
Ben Meadors
2b97576b18 NRF52 BLE fixes / tweaks (#7152)
* Try-fix: Flaky NRF52 bluetooth pairing for some users

* Safe access for screen pointer
2025-06-27 06:26:34 -05:00
Jason P
29e7a71c97 2.7 Miscellaneous Fixes - Week 1 (#7102)
* Update Favorite Node Message Options to unify against other screens

* Rebuild Horizontal Battery, Resolve overlap concerns

* Update positioning on Message frame and fix drawCommonHeader overlay

* Beginnings of creating isHighResolution bool

* Fixup determineResolution()

* Implement isHighResolution in place of SCREEN_WIDTH > 128 checks

* Line Spacing bound to isHighResolution

* Analog Clock for all

* Add AM/PM to Analog Clock if isHighResolution and not TWatch

* Simple Menu Queue, and add time menu

* Fix prompt string for 12/24 hour picker

* More menu banners into functions

* Fix Action Menu on Home frame

* Correct pop-up calculation size and continue to leverage isHighResolution

* Move menu bits to MenuHandler

* Plumb in the digital/analog picker

* Correct Clock Face Picker title

* Clock picker fixes

* Migrate the rest of the menus to MenuHandler.*

* Add compass menu and needle point option

* Minor fix for compass point menu

* Correct Home menu into typical format

* Fix emoji bounce, overlap, and missing commonHeader

* Sanitize long_names and removed unused variables

* Slightly better sanitizeString variation

* Resolved apostrophe being shown as upside down question mark

* Gotta keep height and width in expected order

* Remove Second Hand for Analog Clock on EInk displays

* Fix Clock menu option decision tree

* Improvements to Eink Navigation

* Pause Banner for Eink moved to bottom

* Updated working for 12-/24-hour menu and Added US/Arizona to timezone picker

* Add Adhoc Ping and resolve error with std::string sanitized

* Hide quick toggle as option is available within Action Menu, commented out for the moment

* Remove old battery icon and option, use drawCommonHeader throughout, re-add battery to Clock frames

* fix misc build warnings. NFC

* Update Analog Clock on EInk to show more digits

* Establish Action Menu on all node list screens, add NodeDB reset (with confirmation) option

* Add Toggle Backlight for EInk Displays

* Suppress action screen Full refresh for Eink

* Adjust drawBluetoothConnectedIcon on TWatch

* Maintain clock frame when switching between Clock Faces

* Move modules beyond the clock in navigation

* addressed the conflicts, and changed target branch to 2.7-MiscFixes-Week1

* cleanup, cheers

* Add AM/PM to low resolution clock also

* Small adjustments to AM/PM replacement across various devices

* Resolve dangling pointer issues with sanitize code

* Update comments for Screen.cpp related to module load change

* Trunk runs

* Update message caching to correct aged timestamp

* Menu wording adjustments

* Time Format wording

* Use all the rows on EInk since with autohide the navigation bar

* Finalize Time Format picker word change

* Retired drawFunctionOverlay code

No longer being used

* Actually honor the points-north setting

* Trunk

* Compressed action list

* Update no-op showOverlayBanner function

* trunk

* Correct T_Watch_S3 specific line

* Autosized Action menu per screen

* Finalize Autosized Action menu per screen

* Unify Message Titles

* Reorder Timezones to match expectations

* Adjust text location for pop-ups

* Revert "Actually honor the points-north setting"

This reverts commit 20988aa4fa.

* Make NodeDB sort its internal vector when lastheard is updated. Don't sort in NodeListRenderer

* Update src/graphics/draw/NodeListRenderer.cpp

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

* Update src/mesh/NodeDB.cpp

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

* Pass by reference -- Thanks Copilot!

* Throttle sorting just a touch

* Check more carefully for own node

* Eliminate some now-unneeded sorting

* Move function after include

* Putting Modules back to position 0 and some trunk checks found

* Add Scrollbar for Action menus

* Second attempt to move modules down the navigation bar

* Continue effort of moving modules in the navigation

* Canned Messages tweak

* Replicate Function + Space through the Menu System

* Move init button parameters into config struct (#7145)

* Remove bundling of web-ui from ESP32 devices (#7143)

* Fixed triple click GPS toggle bungle

* Move init button parameters into config struct

* Reapply "Actually honor the points-north setting"

This reverts commit 42c1967e7b.

* Actually do compass pointings correctly

* Tweak to node bearings

* Menu wording tweaks

* Get the compass_north_top logic right

* Don't jump frames after setting Compass

* Get rid of the extra bearingTo functions

* Don't blink Mail on EInk Clock Screens

* Actually set lat and long

* Calibrate

* Convert Radians to Degrees

* More degree vs radians fixes

* De-duplicate draw arrow function

* Don't advertise compass calibration without an accell thread.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: csrutil <keming.cao@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-26 22:11:20 -05:00
Ben Meadors
18fbc2149d Fix iOS bluetooth crash: Ensure UINT32_MAX is not used (#7147) 2025-06-26 19:23:08 -05:00
renovate[bot]
50424d1035 chore(deps): update meshtastic/web to v2.6.4 (#7017)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-26 12:39:03 -05:00
Ben Meadors
e87c991975 Fixed triple click GPS toggle bungle 2025-06-26 11:19:54 -05:00
Ben Meadors
2ab717cebb Remove bundling of web-ui from ESP32 devices (#7143) 2025-06-26 10:57:33 -05:00
Ben Meadors
ad23c065f6 Rate limiting fix and added 2 second rate limiting to text messages (#7139)
* Rate limiting fix and added 1.5 second rate limiting to text messages

* Remove copy-pasta

* Update src/mesh/Default.h

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

* Two is more reasonable

* Two too

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-26 07:56:34 -05:00
dylanli
eeb52a1221 support seeed_wio_tracker_L1_eink (#7125)
* initial commit of eink version

* fit for ssd1682 initial test run hud

* update to solve mirroring problem

* change eink screen ic to ssd1680

* remove HINK_E0213A367

* trunk fmt

* fix wrong type

* fix some fmt
2025-06-26 06:30:45 -05:00
Manuel
8ae05f6b33 defcon tft display size definitions (#7142) 2025-06-26 05:44:51 -05:00
github-actions[bot]
f6630cd31d Upgrade trunk (#7084)
Co-authored-by: sachaw <11172820+sachaw@users.noreply.github.com>
2025-06-26 18:31:14 +10:00
Austin
c144bd03dc MeshAdv-Mini: Correct autoconf settings (#7117) 2025-06-25 20:17:47 -05:00
Ben Meadors
7512673b09 Do not beacon Device telemetry by default anymore (#7116)
* Do not beacon Device telemetry by default anymore

* Update

* Old default interval for sensor

* Added userpref

* Addd tracker to default telemetry roles

* Let the macro do its job in router mode
2025-06-25 16:36:33 -05:00
github-actions[bot]
3870d81bf6 [create-pull-request] automated change (#7134)
Co-authored-by: caveman99 <25002+caveman99@users.noreply.github.com>
2025-06-25 20:18:55 +02:00
Kongduino
a7dcf580ad Update RedirectablePrint.cpp (#7114)
Bug fix to my hexDump code. Because `log()` adds a carriage return, hexdump lines were split over 3 lines. This fixes it.
2025-06-25 19:54:57 +02:00
todd-herbert
ecfaf3a095 Canned Messages via InkHUD menu (#7096)
* Allow observers to respond to AdminMessage requests
Ground work for CannedMessage getters and setters

* Enable CannedMessage config in apps for InkHUD devices

* Migrate the InkHUD::Events AdminModule observer
Use the new AdminModule_ObserverData struct

* Bare-bones NicheGraphics util to access canned messages
Handles loading and parsing. Handle admin messages for setting and getting.

* Send canned messages via on-screen menu

* Change ThreadedMessageApplet from Observer to Module API
Allows us to intercept locally generated packets ('loopbackOK = true'), to handle outgoing canned messages.

* Fix: crash getting empty canned message string via Client API

* Move file into Utils subdir

* Move an include statement from .cpp to .h

* Limit strncpy size of dest, not source
Wasn't critical in ths specific case, but definitely a mistake.
2025-06-25 06:04:18 -05:00
Ben Meadors
91bcf072a0 Tweak interval trottling (#7113) 2025-06-24 05:27:40 -05:00
renovate[bot]
4802cef3ca chore(deps): update radiolib to v7.2.0 (#7098)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-24 15:56:02 +10:00
renovate[bot]
38896198f2 chore(deps): update meshtastic/device-ui digest to cdc6e5b (#7112)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-23 12:04:55 -05:00
Jonathan Bennett
012f88e56f Make the 4-way on the L1 work on press instead of release (#7108) 2025-06-22 20:57:39 -05:00
Jonathan Bennett
0808f5215f fix mismatch between Exclude FSM include names (#7107) 2025-06-22 18:48:16 -05:00
Jonathan Bennett
247e05bb10 Get the unphone to stop bootlooping: increase MAX_THREADS everywhere (#7106) 2025-06-22 16:59:04 -05:00
github-actions[bot]
4308bbc156 automated bumps (#7097)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-06-22 05:54:32 -05:00
Jonathan Bennett
ce1480df98 Initialize value to fix warning 2025-06-21 23:56:14 -05:00
Jonathan Bennett
0108ad7992 Don't write the config unless the setting changed 2025-06-21 23:19:41 -05:00
Jonathan Bennett
e1df4e19e5 Default to very short updownDebounce values 2025-06-21 20:47:11 -05:00
whywilson
8ba98ae873 Add a debounce time parameter and use it in the runOnce method to debounce the key. 2025-06-21 20:40:34 -05:00
whywilson
7a38368494 Optimize key event processing and add debounce logic. 2025-06-21 20:40:24 -05:00
716 changed files with 20889 additions and 6897 deletions

View File

@@ -27,10 +27,10 @@ inputs:
description: A newline separated list of paths to store as artifacts
required: false
default: ""
include-web-ui:
description: Include the web UI in the build
required: false
default: "false"
# include-web-ui:
# description: Include the web UI in the build
# required: false
# default: "false"
arch:
description: Processor arch name
required: true
@@ -43,29 +43,29 @@ runs:
id: base
uses: ./.github/actions/setup-base
- name: Get web ui version
if: inputs.include-web-ui == 'true'
id: webver
shell: bash
run: |
echo "ver=$(cat bin/web.version)" >> $GITHUB_OUTPUT
# - name: Get web ui version
# if: inputs.include-web-ui == 'true'
# id: webver
# shell: bash
# run: |
# echo "ver=$(cat bin/web.version)" >> $GITHUB_OUTPUT
- name: Pull web ui
if: inputs.include-web-ui == 'true'
uses: dsaltares/fetch-gh-release-asset@master
with:
repo: meshtastic/web
file: build.tar
target: build.tar
token: ${{ inputs.github_token }}
version: tags/v${{ steps.webver.outputs.ver }}
# - name: Pull web ui
# if: inputs.include-web-ui == 'true'
# uses: dsaltares/fetch-gh-release-asset@master
# with:
# repo: meshtastic/web
# file: build.tar
# target: build.tar
# token: ${{ inputs.github_token }}
# version: tags/v${{ steps.webver.outputs.ver }}
- name: Unpack web ui
if: inputs.include-web-ui == 'true'
shell: bash
run: |
tar -xf build.tar -C data/static
rm build.tar
# - name: Unpack web ui
# if: inputs.include-web-ui == 'true'
# shell: bash
# run: |
# tar -xf build.tar -C data/static
# rm build.tar
- name: Remove debug flags for release
shell: bash

View File

@@ -5,7 +5,7 @@ runs:
using: composite
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive
path: meshtasticd

View File

@@ -1,37 +0,0 @@
name: Build ESP32
on:
workflow_call:
inputs:
board:
required: true
type: string
permissions: read-all
jobs:
build-esp32:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build ESP32
id: build
uses: ./.github/actions/build-variant
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
board: ${{ inputs.board }}
remove-debug-flags: >-
./arch/esp32/esp32.ini
./arch/esp32/esp32s2.ini
./arch/esp32/esp32s3.ini
./arch/esp32/esp32c3.ini
./arch/esp32/esp32c6.ini
build-script-path: bin/build-esp32.sh
ota-firmware-source: firmware.bin
ota-firmware-target: release/bleota.bin
artifact-paths: |
release/*.bin
release/*.elf
include-web-ui: true
arch: esp32

View File

@@ -1,37 +0,0 @@
name: Build ESP32-C3
on:
workflow_call:
inputs:
board:
required: true
type: string
permissions: read-all
jobs:
build-esp32-c3:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build ESP32-C3
id: build
uses: ./.github/actions/build-variant
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
board: ${{ inputs.board }}
remove-debug-flags: >-
./arch/esp32/esp32.ini
./arch/esp32/esp32s2.ini
./arch/esp32/esp32s3.ini
./arch/esp32/esp32c3.ini
./arch/esp32/esp32c6.ini
build-script-path: bin/build-esp32.sh
ota-firmware-source: firmware-c3.bin
ota-firmware-target: release/bleota-c3.bin
artifact-paths: |
release/*.bin
release/*.elf
include-web-ui: true
arch: esp32c3

View File

@@ -1,37 +0,0 @@
name: Build ESP32-C6
on:
workflow_call:
inputs:
board:
required: true
type: string
permissions: read-all
jobs:
build-esp32-c6:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build ESP32-C6
id: build
uses: ./.github/actions/build-variant
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
board: ${{ inputs.board }}
remove-debug-flags: >-
./arch/esp32/esp32.ini
./arch/esp32/esp32s2.ini
./arch/esp32/esp32s3.ini
./arch/esp32/esp32c3.ini
./arch/esp32/esp32c6.ini
build-script-path: bin/build-esp32.sh
ota-firmware-source: firmware-c3.bin
ota-firmware-target: release/bleota-c3.bin
artifact-paths: |
release/*.bin
release/*.elf
include-web-ui: true
arch: esp32c6

View File

@@ -1,37 +0,0 @@
name: Build ESP32-S3
on:
workflow_call:
inputs:
board:
required: true
type: string
permissions: read-all
jobs:
build-esp32-s3:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build ESP32-S3
id: build
uses: ./.github/actions/build-variant
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
board: ${{ inputs.board }}
remove-debug-flags: >-
./arch/esp32/esp32.ini
./arch/esp32/esp32s2.ini
./arch/esp32/esp32s3.ini
./arch/esp32/esp32c3.ini
./arch/esp32/esp32c6.ini
build-script-path: bin/build-esp32.sh
ota-firmware-source: firmware-s3.bin
ota-firmware-target: release/bleota-s3.bin
artifact-paths: |
release/*.bin
release/*.elf
include-web-ui: true
arch: esp32s3

66
.github/workflows/build_firmware.yml vendored Normal file
View File

@@ -0,0 +1,66 @@
name: Build
on:
workflow_call:
inputs:
version:
required: true
type: string
platform:
required: true
type: string
pio_env:
required: true
type: string
permissions: read-all
jobs:
pio-build:
name: build-${{ inputs.platform }}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Set OTA firmware source and target
if: startsWith(inputs.platform, 'esp32')
id: ota_dir
env:
PIO_PLATFORM: ${{ inputs.platform }}
run: |
if [ "$PIO_PLATFORM" = "esp32s3" ]; then
echo "src=firmware-s3.bin" >> $GITHUB_OUTPUT
echo "tgt=release/bleota-s3.bin" >> $GITHUB_OUTPUT
elif [ "$PIO_PLATFORM" = "esp32c3" ] || [ "$PIO_PLATFORM" = "esp32c6" ]; then
echo "src=firmware-c3.bin" >> $GITHUB_OUTPUT
echo "tgt=release/bleota-c3.bin" >> $GITHUB_OUTPUT
elif [ "$PIO_PLATFORM" = "esp32" ]; then
echo "src=firmware.bin" >> $GITHUB_OUTPUT
echo "tgt=release/bleota.bin" >> $GITHUB_OUTPUT
fi
- name: Build ${{ inputs.platform }}
id: build
uses: meshtastic/gh-action-firmware@main
with:
pio_platform: ${{ inputs.platform }}
pio_env: ${{ inputs.pio_env }}
pio_target: build
ota_firmware_source: ${{ steps.ota_dir.outputs.src || '' }}
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
with:
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip
overwrite: true
path: |
release/*.bin
release/*.elf
release/*.uf2
release/*.hex
release/*-ota.zip

View File

@@ -1,30 +0,0 @@
name: Build NRF52
on:
workflow_call:
inputs:
board:
required: true
type: string
permissions: read-all
jobs:
build-nrf52:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build NRF52
id: build
uses: ./.github/actions/build-variant
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
board: ${{ inputs.board }}
build-script-path: bin/build-nrf52.sh
artifact-paths: |
release/*.hex
release/*.uf2
release/*.elf
release/*.zip
arch: nrf52840

View File

@@ -1,28 +0,0 @@
name: Build RPI2040
on:
workflow_call:
inputs:
board:
required: true
type: string
permissions: read-all
jobs:
build-rpi2040:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Raspberry Pi 2040
id: build
uses: ./.github/actions/build-variant
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
board: ${{ inputs.board }}
build-script-path: bin/build-rpi2040.sh
artifact-paths: |
release/*.uf2
release/*.elf
arch: rp2040

View File

@@ -1,29 +0,0 @@
name: Build STM32
on:
workflow_call:
inputs:
board:
required: true
type: string
permissions: read-all
jobs:
build-stm32:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build STM32WL
id: build
uses: ./.github/actions/build-variant
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
board: ${{ inputs.board }}
build-script-path: bin/build-stm32.sh
artifact-paths: |
release/*.hex
release/*.bin
release/*.elf
arch: stm32

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

@@ -47,7 +47,7 @@ jobs:
runs-on: ${{ inputs.runs-on }}
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -83,7 +83,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{ github.ref }}

View File

@@ -30,18 +30,31 @@ jobs:
strategy:
fail-fast: false
matrix:
arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32, check]
runs-on: ubuntu-latest
arch:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
- check
runs-on: ubuntu-24.04
steps:
- id: checkout
uses: actions/checkout@v4
name: Checkout base
- id: jsonStep
- uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: 3.x
cache: pip
- run: pip install -U platformio
- name: Generate matrix
id: jsonStep
run: |
if [[ "$GITHUB_HEAD_REF" == "" ]]; then
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
else
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} quick)
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} pr)
fi
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF Targets: $TARGETS"
echo "${{matrix.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
@@ -52,9 +65,25 @@ jobs:
esp32c6: ${{ steps.jsonStep.outputs.esp32c6 }}
nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }}
rp2040: ${{ steps.jsonStep.outputs.rp2040 }}
rp2350: ${{ steps.jsonStep.outputs.rp2350 }}
stm32: ${{ steps.jsonStep.outputs.stm32 }}
check: ${{ steps.jsonStep.outputs.check }}
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
id: version
env:
BUILD_LOCATION: local
outputs:
long: ${{ steps.version.outputs.long }}
deb: ${{ steps.version.outputs.deb }}
check:
needs: setup
strategy:
@@ -64,7 +93,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Build base
id: base
uses: ./.github/actions/setup-base
@@ -72,69 +101,95 @@ jobs:
run: bin/check-all.sh ${{ matrix.board }}
build-esp32:
needs: setup
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
uses: ./.github/workflows/build_esp32.yml
uses: ./.github/workflows/build_firmware.yml
with:
board: ${{ matrix.board }}
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32
build-esp32-s3:
needs: setup
build-esp32s3:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
uses: ./.github/workflows/build_esp32_s3.yml
uses: ./.github/workflows/build_firmware.yml
with:
board: ${{ matrix.board }}
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32s3
build-esp32-c3:
needs: setup
build-esp32c3:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
uses: ./.github/workflows/build_esp32_c3.yml
uses: ./.github/workflows/build_firmware.yml
with:
board: ${{ matrix.board }}
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c3
build-esp32-c6:
needs: setup
build-esp32c6:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
uses: ./.github/workflows/build_esp32_c6.yml
uses: ./.github/workflows/build_firmware.yml
with:
board: ${{ matrix.board }}
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c6
build-nrf52:
needs: setup
build-nrf52840:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
uses: ./.github/workflows/build_nrf52.yml
uses: ./.github/workflows/build_firmware.yml
with:
board: ${{ matrix.board }}
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: nrf52840
build-rpi2040:
needs: setup
build-rp2040:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
uses: ./.github/workflows/build_rpi2040.yml
uses: ./.github/workflows/build_firmware.yml
with:
board: ${{ matrix.board }}
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: rp2040
build-rp2350:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.rp2350) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: rp2350
build-stm32:
needs: setup
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
uses: ./.github/workflows/build_stm32.yml
uses: ./.github/workflows/build_firmware.yml
with:
board: ${{ matrix.board }}
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: stm32
build-debian-src:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/build_debian_src.yml
with:
series: UNRELEASED
@@ -209,26 +264,36 @@ jobs:
strategy:
fail-fast: false
matrix:
arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32]
arch:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
runs-on: ubuntu-latest
needs:
[
version,
build-esp32,
build-esp32-s3,
build-esp32-c3,
build-esp32-c6,
build-nrf52,
build-rpi2040,
build-esp32s3,
build-esp32c3,
build-esp32c6,
build-nrf52840,
build-rp2040,
build-rp2350,
build-stm32,
]
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
path: ./
pattern: firmware-${{matrix.arch}}-*
@@ -237,17 +302,13 @@ jobs:
- name: Display structure of downloaded files
run: ls -R
- name: Get release version string
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Move files up
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v4
with:
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true
path: |
./firmware-*.bin
@@ -257,14 +318,13 @@ jobs:
./device-*.sh
./device-*.bat
./littlefs-*.bin
./littlefswebui-*.bin
./bleota*bin
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output
@@ -278,12 +338,12 @@ jobs:
chmod +x ./output/device-update.sh
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip ./output
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip
uses: actions/upload-artifact@v4
with:
name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
overwrite: true
path: ./*.elf
retention-days: 30
@@ -291,8 +351,8 @@ jobs:
- uses: scruplelesswizard/comment-artifact@main
if: ${{ github.event_name == 'pull_request' }}
with:
name: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
description: "Download firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
description: "Download firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
github-token: ${{ secrets.GITHUB_TOKEN }}
release-artifacts:
@@ -301,56 +361,49 @@ jobs:
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
needs:
- version
- gather-artifacts
- build-debian-src
- package-pio-deps-native-tft
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
echo "deb=$(./bin/buildinfo.py deb)" >> $GITHUB_OUTPUT
id: version
env:
BUILD_LOCATION: local
- name: Create release
uses: softprops/action-gh-release@v2
id: create_release
with:
draft: true
prerelease: true
name: Meshtastic Firmware ${{ steps.version.outputs.long }} Alpha
tag_name: v${{ steps.version.outputs.long }}
name: Meshtastic Firmware ${{ needs.version.outputs.long }} Alpha
tag_name: v${{ needs.version.outputs.long }}
body: |
Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
pattern: firmware-debian-${{ steps.version.outputs.deb }}~UNRELEASED-src
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
merge-multiple: true
path: ./output/debian-src
- name: Download `native-tft` pio deps
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
pattern: platformio-deps-native-tft-${{ steps.version.outputs.long }}
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output/pio-deps-native-tft
- name: Zip Linux sources
working-directory: output
run: |
zip -j -9 -r ./meshtasticd-${{ steps.version.outputs.deb }}-src.zip ./debian-src
zip -9 -r ./platformio-deps-native-tft-${{ steps.version.outputs.long }}.zip ./pio-deps-native-tft
zip -j -9 -r ./meshtasticd-${{ needs.version.outputs.deb }}-src.zip ./debian-src
zip -9 -r ./platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip ./pio-deps-native-tft
# For diagnostics
- name: Display structure of downloaded files
@@ -360,8 +413,8 @@ jobs:
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
gh release upload v${{ steps.version.outputs.long }} ./output/meshtasticd-${{ steps.version.outputs.deb }}-src.zip
gh release upload v${{ steps.version.outputs.long }} ./output/platformio-deps-native-tft-${{ steps.version.outputs.long }}.zip
gh release upload v${{ needs.version.outputs.long }} ./output/meshtasticd-${{ needs.version.outputs.deb }}-src.zip
gh release upload v${{ needs.version.outputs.long }} ./output/platformio-deps-native-tft-${{ needs.version.outputs.long }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -369,26 +422,30 @@ jobs:
strategy:
fail-fast: false
matrix:
arch: [esp32, esp32s3, esp32c3, esp32c6, nrf52840, rp2040, stm32]
arch:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [release-artifacts]
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Get release version string
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
pattern: firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output
@@ -401,16 +458,16 @@ jobs:
chmod +x ./output/device-update.sh
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip ./output
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
name: debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
merge-multiple: true
path: ./elfs
- name: Zip debug elfs
run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip ./elfs
run: zip -j -9 -r ./debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./elfs
# For diagnostics
- name: Display structure of downloaded files
@@ -420,33 +477,30 @@ jobs:
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
gh release upload v${{ steps.version.outputs.long }} ./firmware-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
gh release upload v${{ steps.version.outputs.long }} ./debug-elfs-${{matrix.arch}}-${{ steps.version.outputs.long }}.zip
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
gh release upload v${{ needs.version.outputs.long }} ./debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish-firmware:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [release-firmware]
needs: [release-firmware, version]
env:
targets: esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,stm32
targets: |-
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: 3.x
- name: Get release version string
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v5
with:
pattern: firmware-{${{ env.targets }}}-${{ steps.version.outputs.long }}
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./publish
@@ -460,9 +514,9 @@ jobs:
external_repository: meshtastic/meshtastic.github.io
publish_branch: master
publish_dir: ./publish
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ steps.version.outputs.long }}
destination_dir: ${{ env.DEST_PREFIX }}firmware-${{ needs.version.outputs.long }}
keep_files: true
user_name: github-actions[bot]
user_email: github-actions[bot]@users.noreply.github.com
commit_message: ${{ steps.version.outputs.long }}
commit_message: ${{ needs.version.outputs.long }}
enable_jekyll: true

View File

@@ -8,12 +8,13 @@ permissions: read-all
jobs:
trunk_check:
if: github.repository == 'meshtastic/firmware'
name: Trunk Check and Upload
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Trunk Check
uses: trunk-io/trunk-action@v1
@@ -21,6 +22,7 @@ jobs:
trunk-token: ${{ secrets.TRUNK_TOKEN }}
trunk_upgrade:
if: github.repository == 'meshtastic/firmware'
# See: https://github.com/trunk-io/trunk-action/blob/v1/readme.md#automatic-upgrades
name: Trunk Upgrade (PR)
runs-on: ubuntu-24.04
@@ -29,7 +31,7 @@ jobs:
pull-requests: write # For trunk to create PRs
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Trunk Upgrade
uses: trunk-io/trunk-action/upgrade@v1

View File

@@ -34,7 +34,7 @@ jobs:
needs: build-debian-src
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive
path: meshtasticd
@@ -58,7 +58,7 @@ jobs:
id: version
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
merge-multiple: true

View File

@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}

View File

@@ -32,7 +32,7 @@ jobs:
needs: build-debian-src
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: recursive
path: meshtasticd
@@ -60,7 +60,7 @@ jobs:
id: version
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
merge-multiple: true

24
.github/workflows/pr_enforce_labels.yml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Check PR Labels
on:
pull_request:
types: [opened, edited, labeled, unlabeled, synchronize, reopened]
permissions:
pull-requests: read
contents: read
jobs:
check-label:
runs-on: ubuntu-24.04
steps:
- name: Check for PR labels
uses: actions/github-script@v7
with:
script: |
const labels = context.payload.pull_request.labels.map(label => label.name);
const requiredLabels = ['bugfix', 'enhancement', 'hardware-support', 'dependencies', 'submodules', 'github_actions', 'trunk'];
const hasRequiredLabel = labels.some(label => requiredLabels.includes(label));
if (!hasRequiredLabel) {
core.setFailed(`PR must have at least one of the following labels before it can be merged: ${requiredLabels.join(', ')}.`);
}

238
.github/workflows/pr_tests.yml vendored Normal file
View File

@@ -0,0 +1,238 @@
name: Tests
# DISABLED: Changed from automatic PR triggers to manual only
on:
workflow_dispatch:
inputs:
reason:
description: "Reason for manual test run"
required: false
default: "Manual test execution"
concurrency:
group: tests-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read
actions: read
checks: write
pull-requests: write
jobs:
native-tests:
name: "🧪 Native Tests"
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/test_native.yml
permissions:
contents: read
actions: read
checks: write
test-summary:
name: "📊 Test Results"
runs-on: ubuntu-latest
needs: [native-tests]
if: always()
permissions:
contents: read
actions: read
checks: write
pull-requests: write
steps:
- uses: actions/checkout@v5
with:
submodules: recursive
- name: Get release version string
run: echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Download test artifacts
if: needs.native-tests.result != 'skipped'
uses: actions/download-artifact@v5
with:
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
merge-multiple: true
- name: Parse test results and create detailed summary
id: test-results
run: |
echo "## 🧪 Test Results Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Check overall job status first
if [[ "${{ needs.native-tests.result }}" == "success" ]]; then
echo "✅ **Overall Status**: PASSED" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.native-tests.result }}" == "failure" ]]; then
echo "❌ **Overall Status**: FAILED" >> $GITHUB_STEP_SUMMARY
elif [[ "${{ needs.native-tests.result }}" == "cancelled" ]]; then
echo "⏸️ **Overall Status**: CANCELLED" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Tests were cancelled before completion." >> $GITHUB_STEP_SUMMARY
exit 0
else
echo "⚠️ **Overall Status**: SKIPPED" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Tests were skipped." >> $GITHUB_STEP_SUMMARY
exit 0
fi
echo "" >> $GITHUB_STEP_SUMMARY
# Parse detailed test results if available
if [ -f "testreport.xml" ]; then
echo "### 🔍 Individual Test Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
python3 << 'EOF'
import xml.etree.ElementTree as ET
import os
try:
tree = ET.parse('testreport.xml')
root = tree.getroot()
total_tests = 0
passed_tests = 0
failed_tests = 0
skipped_tests = 0
# Parse testsuite elements
for testsuite in root.findall('.//testsuite'):
suite_name = testsuite.get('name', 'Unknown')
suite_tests = int(testsuite.get('tests', '0'))
suite_failures = int(testsuite.get('failures', '0'))
suite_errors = int(testsuite.get('errors', '0'))
suite_skipped = int(testsuite.get('skipped', '0'))
total_tests += suite_tests
failed_tests += suite_failures + suite_errors
skipped_tests += suite_skipped
passed_tests += suite_tests - suite_failures - suite_errors - suite_skipped
if suite_tests > 0:
status = "✅" if (suite_failures + suite_errors) == 0 else "❌"
print(f"**{status} Test Suite: {suite_name}**")
print(f"- Total: {suite_tests}")
print(f"- Passed: ✅ {suite_tests - suite_failures - suite_errors - suite_skipped}")
print(f"- Failed: ❌ {suite_failures + suite_errors}")
if suite_skipped > 0:
print(f"- Skipped: ⏭️ {suite_skipped}")
print("")
# Show individual test results for failed suites
if suite_failures + suite_errors > 0:
print("**Failed Tests:**")
for testcase in testsuite.findall('testcase'):
test_name = testcase.get('name', 'Unknown')
failure = testcase.find('failure')
error = testcase.find('error')
if failure is not None:
msg = failure.get('message', 'Unknown error')[:100]
print(f"- ❌ `{test_name}`: {msg}")
elif error is not None:
msg = error.get('message', 'Unknown error')[:100]
print(f"- ❌ `{test_name}`: ERROR - {msg}")
print("")
else:
# Show passed tests for successful suites
passed_count = 0
for testcase in testsuite.findall('testcase'):
if testcase.find('failure') is None and testcase.find('error') is None:
if passed_count < 5: # Limit to first 5 to avoid spam
test_name = testcase.get('name', 'Unknown')
print(f"- ✅ `{test_name}`: PASSED")
passed_count += 1
if passed_count > 5:
print(f"- ... and {passed_count - 5} more tests passed")
print("")
# Summary statistics
print("### 📊 Test Statistics")
print(f"- **Total Tests**: {total_tests}")
print(f"- **Passed**: ✅ {passed_tests}")
print(f"- **Failed**: ❌ {failed_tests}")
if skipped_tests > 0:
print(f"- **Skipped**: ⏭️ {skipped_tests}")
if failed_tests > 0:
print(f"\n❌ **{failed_tests} tests failed out of {total_tests} total**")
else:
print(f"\n✅ **All {total_tests} tests passed!**")
except Exception as e:
print(f"❌ Error parsing test results: {e}")
EOF
else
echo "⚠️ **No detailed test report available**" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Test artifacts may not have been generated properly." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "View detailed logs in the [Actions tab](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
- name: Comment test results on PR
if: github.event_name == 'pull_request' && needs.native-tests.result != 'skipped'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
// Read the step summary to use as PR comment
let testSummary = "## 🧪 Test Results Summary\n\n";
if ("${{ needs.native-tests.result }}" === "success") {
testSummary += "✅ **All tests passed!**\n\n";
} else if ("${{ needs.native-tests.result }}" === "failure") {
testSummary += "❌ **Some tests failed.**\n\n";
} else {
testSummary += "⚠️ **Tests did not complete normally.**\n\n";
}
testSummary += `View detailed results: [Actions Run](${context.payload.repository.html_url}/actions/runs/${context.runId})\n\n`;
testSummary += "---\n";
testSummary += "*This comment will be automatically updated when new commits are pushed.*";
// Find existing comment
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
const botComment = comments.data.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('🧪 Test Results Summary')
);
if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: testSummary
});
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: testSummary
});
}
- name: Set overall status
run: |
if [[ "${{ needs.native-tests.result }}" == "success" ]]; then
echo "All tests passed! ✅"
exit 0
else
echo "Some tests failed! ❌"
exit 1
fi

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: |-
@@ -56,7 +60,7 @@ jobs:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v5
@@ -99,8 +103,9 @@ jobs:
with:
base: ${{ github.event.repository.default_branch }}
branch: create-pull-request/bump-version
labels: github_actions
title: Bump release version
commit-message: automated bumps
commit-message: Automated version bumps
add-paths: |
version.properties
debian/changelog

View File

@@ -13,6 +13,7 @@ permissions:
jobs:
semgrep-full:
if: github.repository == 'meshtastic/firmware'
runs-on: ubuntu-24.04
container:
image: semgrep/semgrep
@@ -20,7 +21,7 @@ jobs:
steps:
# step 1
- name: clone application source code
uses: actions/checkout@v4
uses: actions/checkout@v5
# step 2
- name: full scan

View File

@@ -13,7 +13,7 @@ jobs:
steps:
# step 1
- name: clone application source code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0

View File

@@ -11,6 +11,7 @@ permissions:
jobs:
stale_issues:
if: github.repository == 'meshtastic/firmware'
name: Close Stale Issues
runs-on: ubuntu-latest

View File

@@ -14,7 +14,7 @@ jobs:
name: Native Simulator Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -70,7 +70,7 @@ jobs:
name: Native PlatformIO Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -127,7 +127,7 @@ jobs:
- platformio-tests
if: always()
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -137,20 +137,20 @@ jobs:
id: version
- name: Download test artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
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
reporter: java-junit
- name: Download coverage artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}.zip
path: code-coverage-report

View File

@@ -12,13 +12,15 @@ permissions:
jobs:
native-tests:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/test_native.yml
hardware-tests:
if: github.repository == 'meshtastic/firmware'
runs-on: test-runner
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
# - uses: actions/setup-python@v5
# with:

View File

@@ -18,7 +18,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Trunk Check
uses: trunk-io/trunk-action@v1

View File

@@ -16,7 +16,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v5
- name: Trunk Check
uses: trunk-io/trunk-action@v1

View File

@@ -15,7 +15,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}

View File

@@ -11,7 +11,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: true
@@ -34,7 +34,9 @@ jobs:
uses: peter-evans/create-pull-request@v7
with:
branch: create-pull-request/update-protobufs
labels: submodules
title: Update protobufs and classes
commit-message: Update protobufs
add-paths: |
protobufs
src/mesh

5
.gitignore vendored
View File

@@ -37,4 +37,7 @@ release/
.vscode/extensions.json
/compile_commands.json
src/mesh/raspihttp/certificate.pem
src/mesh/raspihttp/private_key.pem
src/mesh/raspihttp/private_key.pem
# Ignore logo (set at build time with platformio-custom.py)
data/boot/logo.*

View File

@@ -1,34 +1,34 @@
version: 0.1
cli:
version: 1.24.0
version: 1.25.0
plugins:
sources:
- id: trunk
ref: v1.7.0
ref: v1.7.2
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- checkov@3.2.442
- renovate@40.60.3
- prettier@3.5.3
- trufflehog@3.89.2
- checkov@3.2.467
- renovate@41.91.3
- prettier@3.6.2
- trufflehog@3.90.5
- yamllint@1.37.1
- bandit@1.8.5
- trivy@0.63.0
- taplo@0.9.3
- ruff@0.12.0
- bandit@1.8.6
- trivy@0.65.0
- taplo@0.10.0
- ruff@0.12.11
- isort@6.0.1
- markdownlint@0.45.0
- oxipng@9.1.5
- svgo@3.3.2
- svgo@4.0.0
- actionlint@1.7.7
- flake8@7.2.0
- flake8@7.3.0
- hadolint@2.12.1-beta
- shfmt@3.6.0
- shellcheck@0.10.0
- shellcheck@0.11.0
- black@25.1.0
- git-diff-check
- gitleaks@8.27.2
- gitleaks@8.28.0
- clang-format@16.0.3
ignore:
- linters: [ALL]

View File

@@ -3,7 +3,7 @@
# trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
FROM python:3.13-bookworm AS builder
FROM python:3.13-slim-trixie AS builder
ARG PIO_ENV=native
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Etc/UTC
@@ -36,7 +36,7 @@ RUN curl -L "https://github.com/meshtastic/web/releases/download/v$(cat /tmp/fir
##### PRODUCTION BUILD #############
FROM debian:bookworm-slim
FROM debian:trixie-slim
LABEL org.opencontainers.image.title="Meshtastic" \
org.opencontainers.image.description="Debian Meshtastic daemon and web interface" \
org.opencontainers.image.url="https://meshtastic.org" \
@@ -51,8 +51,8 @@ ENV TZ=Etc/UTC
USER root
RUN apt-get update && apt-get --no-install-recommends -y install \
libc-bin libc6 libgpiod2 libyaml-cpp0.7 libi2c0 libuv1 libusb-1.0-0-dev \
liborcania2.3 libulfius2.7 libssl3 \
libc-bin libc6 libgpiod3 libyaml-cpp0.8 libi2c0 libuv1t64 libusb-1.0-0-dev \
liborcania2.3 libulfius2.7t64 libssl3t64 \
libx11-6 libinput10 libxkbcommon-x11-0 \
&& apt-get clean && rm -rf /var/lib/apt/lists/* \
&& mkdir -p /var/lib/meshtasticd \
@@ -61,7 +61,7 @@ RUN apt-get update && apt-get --no-install-recommends -y install \
# Fetch compiled binary from the builder
COPY --from=builder /tmp/firmware/release/meshtasticd /usr/bin/
COPY --from=builder /tmp/web /usr/share/meshtasticd/
COPY --from=builder /tmp/web /usr/share/meshtasticd/web/
# Copy config templates
COPY ./bin/config.d /etc/meshtasticd/available.d

View File

@@ -49,13 +49,13 @@ lib_deps =
${environmental_extra.lib_deps}
${radiolib_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master
https://github.com/meshtastic/esp32_https_server/archive/896f1771ceb5979987a0b41028bf1b4e7aad419b.zip
https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
h2zero/NimBLE-Arduino@^1.4.3
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
lewisxhe/XPowersLib@^0.2.7
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
https://github.com/lewisxhe/XPowersLib/archive/v0.3.0.zip
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto

View File

@@ -28,7 +28,7 @@ lib_deps =
${environmental_extra.lib_deps}
${radiolib_base.lib_deps}
# renovate: datasource=custom.pio depName=XPowersLib packageName=lewisxhe/library/XPowersLib
lewisxhe/XPowersLib@^0.2.7
lewisxhe/XPowersLib@0.3.0
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto

View File

@@ -23,7 +23,7 @@ build_flags =
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2xx0> -<mesh/eth/> -<mesh/raspihttp>
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2xx0> -<mesh/eth/> -<mesh/raspihttp> -<serialization/>
lib_deps=
${arduino_base.lib_deps}

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/c490bcd019e0658404088a61b96e653c9da22c45.zip
framework = arduino
build_src_filter =
@@ -17,7 +17,6 @@ build_src_filter =
+<mesh/raspihttp/>
-<mesh/eth/>
-<modules/esp32>
+<../variants/portduino>
lib_deps =
${env.lib_deps}
@@ -30,14 +29,17 @@ 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}
-D ARCH_PORTDUINO
-fPIC
-Isrc/platform/portduino
-DRADIOLIB_EEPROM_UNSUPPORTED
-DPORTDUINO_LINUX_HARDWARE
-DHAS_UDP_MULTICAST
-DHAS_UDP_MULTICAST=1
-lpthread
-lstdc++fs
-lbluetooth
@@ -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

@@ -2,7 +2,7 @@
extends = arduino_base
platform =
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
platformio/ststm32@19.2.0
platformio/ststm32@19.3.0
platform_packages =
# TODO renovate
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
@@ -16,18 +16,27 @@ build_flags =
${arduino_base.build_flags}
-flto
-Isrc/platform/stm32wl -g
-DMESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
-DMESHTASTIC_EXCLUDE_INPUTBROKER
-DMESHTASTIC_EXCLUDE_I2C
-DMESHTASTIC_EXCLUDE_POWERMON
-DMESHTASTIC_EXCLUDE_SCREEN
-DMESHTASTIC_EXCLUDE_MQTT
-DMESHTASTIC_EXCLUDE_BLUETOOTH
-DMESHTASTIC_EXCLUDE_GPS
;-DDEBUG_MUTE
-DMESHTASTIC_EXCLUDE_AUDIO=1
-DMESHTASTIC_EXCLUDE_ATAK=1 ; ATAK is quite big, disable it for big flash savings.
-DMESHTASTIC_EXCLUDE_INPUTBROKER=1
-DMESHTASTIC_EXCLUDE_POWERMON=1
-DMESHTASTIC_EXCLUDE_SCREEN=1
-DMESHTASTIC_EXCLUDE_MQTT=1
-DMESHTASTIC_EXCLUDE_BLUETOOTH=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 ; 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>
@@ -39,9 +48,9 @@ debug_tool = stlink
lib_deps =
${env.lib_deps}
${radiolib_base.lib_deps}
# renovate: datasource=git-refs depName=caveman99-stm32-Crypto packageName=https://github.com/caveman99/Crypto gitBranch=main
https://github.com/caveman99/Crypto/archive/eae9c768054118a9399690f8af202853d1ae8516.zip
https://github.com/caveman99/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
lib_ignore =
mathertel/OneButton@2.6.1
Wire
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.*
@@ -34,11 +34,12 @@ SRCBIN=.pio/build/$1/firmware.bin
cp $SRCBIN $OUTDIR/$basename-update.bin
echo "Building Filesystem for ESP32 targets"
pio run --environment $1 -t buildfs
cp .pio/build/$1/littlefs.bin $OUTDIR/littlefswebui-$1-$VERSION.bin
# Remove webserver files from the filesystem and rebuild
ls -l data/static # Diagnostic list of files
rm -rf data/static
# If you want to build the webui, uncomment the following lines
# pio run --environment $1 -t buildfs
# cp .pio/build/$1/littlefs.bin $OUTDIR/littlefswebui-$1-$VERSION.bin
# # Remove webserver files from the filesystem and rebuild
# ls -l data/static # Diagnostic list of files
# rm -rf data/static
pio run --environment $1 -t buildfs
cp .pio/build/$1/littlefs.bin $OUTDIR/littlefs-$1-$VERSION.bin
cp bin/device-install.* $OUTDIR

View File

@@ -11,7 +11,7 @@ elif (echo $2 | grep -q "nrf52"); then
elif (echo $2 | grep -q "stm32"); then
bin/build-stm32.sh $1
elif (echo $2 | grep -q "rpi2040"); then
bin/build-rpi2040.sh $1
bin/build-rp2xx0.sh $1
else
echo "Unknown target $2"
exit 1

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

@@ -22,5 +22,5 @@ Input:
TrackballLeft: 5
TrackballRight: 26
TrackballPress: 13
TrackballDirection: FALLING
# User: 21

View File

@@ -6,6 +6,6 @@ Lora:
IRQ: 16
Busy: 20
Reset: 24
TXen: 13
RXen: 12
DIO2_AS_RF_SWITCH: true
DIO3_TCXO_VOLTAGE: true

View File

@@ -9,13 +9,4 @@ Lora:
DIO3_TCXO_VOLTAGE: true
DIO2_AS_RF_SWITCH: true
spidev: spidev0.0
# CS: 8
### RAK13300in Slot 2 pins
# IRQ: 18 #IO6
# Reset: 24 # IO4
# Busy: 19 # IO5
# # Ant_sw: 23 # IO3
# spidev: spidev0.1
# # CS: 7
# CS: 8

View File

@@ -0,0 +1,8 @@
Lora:
### RAK13300in Slot 2 pins
IRQ: 18 #IO6
Reset: 24 # IO4
Busy: 19 # IO5
# Ant_sw: 23 # IO3
spidev: spidev0.1
# CS: 7

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

@@ -5,7 +5,6 @@ TITLE Meshtastic device-install
SET "SCRIPT_NAME=%~nx0"
SET "DEBUG=0"
SET "PYTHON="
SET "WEB_APP=0"
SET "TFT_BUILD=0"
SET "BIGDB8=0"
SET "BIGDB16=0"
@@ -15,17 +14,17 @@ SET "LOGCOUNTER=0"
SET "BPS_RESET=0"
@REM FIXME: Determine mcu from PlatformIO variant, this is unmaintainable.
SET "S3=s3 v3 t-deck wireless-paper wireless-tracker station-g2 unphone"
SET "S3=s3 v3 t-deck wireless-paper wireless-tracker station-g2 unphone t-eth-elite tlora-pager mesh-tab dreamcatcher ESP32-S3-Pico seeed-sensecap-indicator heltec_capsule_sensor_v3 vision-master icarus tracksenger elecrow-adv"
SET "C3=esp32c3"
@REM FIXME: Determine flash size from PlatformIO variant, this is unmaintainable.
SET "BIGDB_8MB=picomputer-s3 unphone seeed-sensecap-indicator crowpanel-esp32s3 heltec_capsule_sensor_v3 heltec-v3 heltec-vision-master-e213 heltec-vision-master-e290 heltec-vision-master-t190 heltec-wireless-paper heltec-wireless-tracker heltec-wsl-v3 icarus seeed-xiao-s3 tbeam-s3-core tracksenger"
SET "BIGDB_16MB=t-deck mesh-tab t-energy-s3 dreamcatcher ESP32-S3-Pico m5stack-cores3 station-g2 t-eth-elite t-watch-s3"
SET "BIGDB_16MB=t-deck mesh-tab t-energy-s3 dreamcatcher ESP32-S3-Pico m5stack-cores3 station-g2 t-eth-elite tlora-pager t-watch-s3 elecrow-adv"
GOTO getopts
:help
ECHO Flash image file to device, but first erasing and writing system information.
ECHO.
ECHO Usage: %SCRIPT_NAME% -f filename [-p PORT] [-P python] (--web) [--1200bps-reset]
ECHO Usage: %SCRIPT_NAME% -f filename [-p PORT] [-P python] [--1200bps-reset]
ECHO.
ECHO Options:
ECHO -f filename The firmware .bin file to flash. Custom to your device type and region. (required)
@@ -35,13 +34,12 @@ ECHO If not set, ESPTOOL iterates all ports (Dangerous).
ECHO -P python Specify alternate python interpreter to use to invoke esptool. (default: python)
ECHO If supplied the script will use python.
ECHO If not supplied the script will try to find esptool in Path.
ECHO --web Enable WebUI. (default: false)
ECHO --1200bps-reset Attempt to place the device in correct mode. (1200bps Reset)
ECHO Some hardware requires this twice.
ECHO.
ECHO Example: %SCRIPT_NAME% -p COM17 --1200bps-reset
ECHO Example: %SCRIPT_NAME% -f firmware-t-deck-tft-2.6.0.0b106d4.bin -p COM11
ECHO Example: %SCRIPT_NAME% -f firmware-unphone-2.6.0.0b106d4.bin -p COM11 --web
ECHO Example: %SCRIPT_NAME% -f firmware-unphone-2.6.0.0b106d4.bin -p COM11
GOTO eof
:version
@@ -61,7 +59,6 @@ IF /I "%~1"=="-f" SET "FILENAME=%~2" & SHIFT
IF "%~1"=="-p" SET "ESPTOOL_PORT=%~2" & SHIFT
IF /I "%~1"=="--port" SET "ESPTOOL_PORT=%~2" & SHIFT
IF "%~1"=="-P" SET "PYTHON=%~2" & SHIFT
IF /I "%~1"=="--web" SET "WEB_APP=1"
IF /I "%~1"=="--1200bps-reset" SET "BPS_RESET=1"
SHIFT
GOTO getopts
@@ -103,7 +100,6 @@ IF NOT "!FILENAME:update=!"=="!FILENAME!" (
)
:skip-filename
SET "ESPTOOL_BAUD=1200"
CALL :LOG_MESSAGE DEBUG "Determine the correct esptool command to use..."
IF NOT "__%PYTHON%__"=="____" (
@@ -145,7 +141,7 @@ CALL :LOG_MESSAGE INFO "Using esptool baud: !ESPTOOL_BAUD!."
IF %BPS_RESET% EQU 1 (
@REM Attempt to change mode via 1200bps Reset.
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! --after no_reset read_flash_status
CALL :RUN_ESPTOOL 1200 --after no_reset read_flash_status
GOTO eof
)
@@ -153,9 +149,6 @@ IF %BPS_RESET% EQU 1 (
@REM https://github.com/meshtastic/web-flasher/blob/main/types/resources.ts#L3
IF NOT "!FILENAME:-tft-=!"=="!FILENAME!" (
CALL :LOG_MESSAGE DEBUG "We are working with a *-tft-* file. !FILENAME!"
IF %WEB_APP% EQU 1 (
CALL :LOG_MESSAGE ERROR "Cannot enable WebUI (--web) and MUI." & GOTO eof
)
SET "TFT_BUILD=1"
) ELSE (
CALL :LOG_MESSAGE DEBUG "We are NOT working with a *-tft-* file. !FILENAME!"
@@ -209,13 +202,8 @@ SET "OTA_FILENAME=bleota.bin"
:end_loop_c3
CALL :LOG_MESSAGE DEBUG "Set OTA_FILENAME to: !OTA_FILENAME!"
@REM Check if (--web) is enabled and prefix BASENAME with "littlefswebui-" else "littlefs-".
IF %WEB_APP% EQU 1 (
CALL :LOG_MESSAGE INFO "WebUI selected."
SET "SPIFFS_FILENAME=littlefswebui-%BASENAME%"
) ELSE (
SET "SPIFFS_FILENAME=littlefs-%BASENAME%"
)
@REM Set SPIFFS filename with "littlefs-" prefix.
SET "SPIFFS_FILENAME=littlefs-%BASENAME%"
CALL :LOG_MESSAGE DEBUG "Set SPIFFS_FILENAME to: !SPIFFS_FILENAME!"
@REM Default offsets.
@@ -245,14 +233,14 @@ IF NOT EXIST !SPIFFS_FILENAME! CALL :LOG_MESSAGE ERROR "File does not exist: "!S
@REM Flashing operations.
CALL :LOG_MESSAGE INFO "Trying to flash "!FILENAME!", but first erasing and writing system information..."
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! erase_flash || GOTO eof
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write_flash 0x00 "!FILENAME!" || GOTO eof
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! erase-flash || GOTO eof
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write-flash 0x00 "!FILENAME!" || GOTO eof
CALL :LOG_MESSAGE INFO "Trying to flash BLEOTA "!OTA_FILENAME!" at OTA_OFFSET !OTA_OFFSET!..."
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write_flash !OTA_OFFSET! "!OTA_FILENAME!" || GOTO eof
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write-flash !OTA_OFFSET! "!OTA_FILENAME!" || GOTO eof
CALL :LOG_MESSAGE INFO "Trying to flash SPIFFS "!SPIFFS_FILENAME!" at SPIFFS_OFFSET !SPIFFS_OFFSET!..."
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write_flash !SPIFFS_OFFSET! "!SPIFFS_FILENAME!" || GOTO eof
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write-flash !SPIFFS_OFFSET! "!SPIFFS_FILENAME!" || GOTO eof
CALL :LOG_MESSAGE INFO "Script complete!."
@@ -264,9 +252,9 @@ EXIT /B %ERRORLEVEL%
:RUN_ESPTOOL
@REM Subroutine used to run ESPTOOL_CMD with arguments.
@REM Also handles %ERRORLEVEL%.
@REM CALL :RUN_ESPTOOL [Baud] [erase_flash|write_flash] [OFFSET] [Filename]
@REM CALL :RUN_ESPTOOL [Baud] [erase_flash|write-flash] [OFFSET] [Filename]
@REM.
@REM Example:: CALL :RUN_ESPTOOL 115200 write_flash 0x10000 "firmwarefile.bin"
@REM Example:: CALL :RUN_ESPTOOL 115200 write-flash 0x10000 "firmwarefile.bin"
IF %DEBUG% EQU 1 CALL :LOG_MESSAGE DEBUG "About to run command: !ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4"
CALL :RESET_ERROR
!ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4

View File

@@ -1,7 +1,6 @@
#!/bin/bash
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
WEB_APP=false
BPS_RESET=false
TFT_BUILD=false
MCU=""
@@ -34,10 +33,9 @@ BIGDB_16MB=(
"m5stack-cores3"
"station-g2"
"t-eth-elite"
"tlora-pager"
"t-watch-s3"
"elecrow-adv-35-tft"
"elecrow-adv-24-28-tft"
"elecrow-adv1-43-50-70-tft"
"elecrow-adv"
)
S3_VARIANTS=(
"s3"
@@ -48,6 +46,7 @@ S3_VARIANTS=(
"station-g2"
"unphone"
"t-eth-elite"
"tlora-pager"
"mesh-tab"
"dreamcatcher"
"ESP32-S3-Pico"
@@ -76,14 +75,13 @@ set -e
# Usage info
show_help() {
cat <<EOF
Usage: $(basename "$0") [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME] [--web] [--1200bps-reset]
Usage: $(basename "$0") [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME] [--1200bps-reset]
Flash image file to device, but first erasing and writing system information.
-h Display this help and exit.
-p ESPTOOL_PORT Set the environment variable for ESPTOOL_PORT. If not set, ESPTOOL iterates all ports (Dangerous).
-P PYTHON Specify alternate python interpreter to use to invoke esptool. (Default: "$PYTHON")
-f FILENAME The firmware .bin file to flash. Custom to your device type and region.
--web Enable WebUI. (Default: false)
--1200bps-reset Attempt to place the device in correct mode. Some hardware requires this twice. (1200bps Reset)
EOF
@@ -107,9 +105,6 @@ while [ $# -gt 0 ]; do
FILENAME="$2"
shift
;;
--web)
WEB_APP=true
;;
--1200bps-reset)
BPS_RESET=true
;;
@@ -140,20 +135,16 @@ if [[ "$FILENAME" != firmware-* ]]; then
exit 1
fi
# Check if FILENAME contains "-tft-" and prevent web/mui comingling.
# Check if FILENAME contains "-tft-" and set target partitionScheme accordingly.
if [[ "${FILENAME//-tft-/}" != "$FILENAME" ]]; then
TFT_BUILD=true
if [[ $WEB_APP == true ]] && [[ $TFT_BUILD == true ]]; then
echo "Cannot enable WebUI (--web) and MUI."
exit 1
fi
fi
# Extract BASENAME from %FILENAME% for later use.
BASENAME="${FILENAME/firmware-/}"
if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
# Default littlefs* offset (--web).
# Default littlefs* offset.
OFFSET=0x300000
# Default OTA Offset
@@ -193,12 +184,8 @@ if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
OTAFILE=bleota-s3.bin
fi
# Check if WEB_APP (--web) is enabled and add "littlefswebui-" to BASENAME else "littlefs-".
if [ "$WEB_APP" = true ]; then
SPIFFSFILE=littlefswebui-${BASENAME}
else
SPIFFSFILE=littlefs-${BASENAME}
fi
# Set SPIFFS filename with "littlefs-" prefix.
SPIFFSFILE=littlefs-${BASENAME}
if [[ ! -f "$FILENAME" ]]; then
echo "Error: file ${FILENAME} wasn't found. Terminating."
@@ -214,12 +201,12 @@ if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
fi
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
$ESPTOOL_CMD erase_flash
$ESPTOOL_CMD write_flash 0x00 "${FILENAME}"
$ESPTOOL_CMD erase-flash
$ESPTOOL_CMD write-flash 0x00 "${FILENAME}"
echo "Trying to flash ${OTAFILE} at offset ${OTA_OFFSET}"
$ESPTOOL_CMD write_flash $OTA_OFFSET "${OTAFILE}"
$ESPTOOL_CMD write-flash $OTA_OFFSET "${OTAFILE}"
echo "Trying to flash ${SPIFFSFILE}, at offset ${OFFSET}"
$ESPTOOL_CMD write_flash $OFFSET "${SPIFFSFILE}"
$ESPTOOL_CMD write-flash $OFFSET "${SPIFFSFILE}"
else
show_help

View File

@@ -133,7 +133,7 @@ IF %CHANGE_MODE% EQU 1 (
@REM Flashing operations.
CALL :LOG_MESSAGE INFO "Trying to flash update "!FILENAME!" at OFFSET 0x10000..."
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write_flash 0x10000 "!FILENAME!" || GOTO eof
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! write-flash 0x10000 "!FILENAME!" || GOTO eof
CALL :LOG_MESSAGE INFO "Script complete!."
@@ -145,9 +145,9 @@ EXIT /B %ERRORLEVEL%
:RUN_ESPTOOL
@REM Subroutine used to run ESPTOOL_CMD with arguments.
@REM Also handles %ERRORLEVEL%.
@REM CALL :RUN_ESPTOOL [Baud] [erase_flash|write_flash] [OFFSET] [Filename]
@REM CALL :RUN_ESPTOOL [Baud] [erase-flash|write-flash] [OFFSET] [Filename]
@REM.
@REM Example:: CALL :RUN_ESPTOOL 115200 write_flash 0x10000 "firmwarefile.bin"
@REM Example:: CALL :RUN_ESPTOOL 115200 write-flash 0x10000 "firmwarefile.bin"
IF %DEBUG% EQU 1 CALL :LOG_MESSAGE DEBUG "About to run command: !ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4"
CALL :RESET_ERROR
!ESPTOOL_CMD! --baud %~1 %~2 %~3 %~4

View File

@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
CHANGE_MODE=false
@@ -30,6 +30,17 @@ Flash image file to device, leave existing system intact."
EOF
}
# Check for --change-mode and remove it from arguments
NEW_ARGS=()
for arg in "$@"; do
if [ "$arg" = "--change-mode" ]; then
CHANGE_MODE=true
else
NEW_ARGS+=("$arg")
fi
done
set -- "${NEW_ARGS[@]}"
while getopts ":hp:P:f:" opt; do
case "${opt}" in
@@ -43,9 +54,6 @@ while getopts ":hp:P:f:" opt; do
;;
f) FILENAME=${OPTARG}
;;
--change-mode)
CHANGE_MODE=true
;;
*)
echo "Invalid flag."
show_help >&2
@@ -55,7 +63,7 @@ while getopts ":hp:P:f:" opt; do
done
shift "$((OPTIND-1))"
if [[ $CHANGE_MODE == true ]]; then
if [ "$CHANGE_MODE" = true ]; then
$ESPTOOL_CMD --baud 1200 --after no_reset read_flash_status
exit 0
fi
@@ -67,7 +75,7 @@ fi
if [ -f "${FILENAME}" ] && [ -z "${FILENAME##*"update"*}" ]; then
echo "Trying to flash update ${FILENAME}"
$ESPTOOL_CMD --baud 115200 write_flash 0x10000 "${FILENAME}"
$ESPTOOL_CMD --baud 115200 write-flash 0x10000 "${FILENAME}"
else
show_help
echo "Invalid file: ${FILENAME}"

View File

@@ -2,50 +2,71 @@
"""Generate the CI matrix."""
import configparser
import json
import os
import sys
import random
rootdir = "variants/"
import re
from platformio.project.config import ProjectConfig
options = sys.argv[1:]
outlist = []
if len(options) < 1:
print(json.dumps(outlist))
exit()
print(json.dumps(outlist))
exit(1)
for subdir, dirs, files in os.walk(rootdir):
for file in files:
if file == "platformio.ini":
config = configparser.ConfigParser()
config.read(subdir + "/" + file)
for c in config.sections():
if c.startswith("env:"):
section = config[c].name[4:]
if "extends" in config[config[c].name]:
if options[0] + "_base" in config[config[c].name]["extends"]:
if "board_level" in config[config[c].name]:
if (
config[config[c].name]["board_level"] == "extra"
) & ("extra" in options):
outlist.append(section)
else:
outlist.append(section)
# Add the TFT variants if the base variant is selected
elif section.replace("-tft", "") in outlist and config[config[c].name].get("board_level") != "extra":
outlist.append(section)
elif section.replace("-inkhud", "") in outlist and config[config[c].name].get("board_level") != "extra":
outlist.append(section)
if "board_check" in config[config[c].name]:
if (config[config[c].name]["board_check"] == "true") & (
"check" in options
):
outlist.append(section)
if ("quick" in options) & (len(outlist) > 3):
print(json.dumps(random.sample(outlist, 3)))
cfg = ProjectConfig.get_instance()
pio_envs = cfg.envs()
# Gather all PlatformIO environments for filtering later
all_envs = []
for pio_env in pio_envs:
env_build_flags = cfg.get(f"env:{pio_env}", 'build_flags')
env_platform = None
for flag in env_build_flags:
# Extract the platform from the build flags
# Example flag: -I variants/esp32s3/heltec-v3
match = re.search(r"-I\s?variants/([^/]+)", flag)
if match:
env_platform = match.group(1)
break
# Intentionally fail if platform cannot be determined
if not env_platform:
print(f"Error: Could not determine platform for environment '{pio_env}'")
exit(1)
# Store env details as a dictionary, and add to 'all_envs' list
env = {
'name': pio_env,
'platform': env_platform,
'board_level': cfg.get(f"env:{pio_env}", 'board_level', default=None),
'board_check': bool(cfg.get(f"env:{pio_env}", 'board_check', default=False))
}
all_envs.append(env)
# Filter outputs based on options
# Check is mutually exclusive with other options (except 'pr')
if "check" in options:
for env in all_envs:
if env['board_check']:
if "pr" in options:
if env['board_level'] == 'pr':
outlist.append(env['name'])
else:
outlist.append(env['name'])
# Filter (non-check) builds by platform
else:
print(json.dumps(outlist))
for env in all_envs:
if options[0] == env['platform']:
# Always include board_level = 'pr'
if env['board_level'] == 'pr':
outlist.append(env['name'])
# Include board_level = 'extra' when requested
elif "extra" in options and env['board_level'] == "extra":
outlist.append(env['name'])
# If no board level is specified, include in release builds (not PR)
elif "pr" not in options and not env['board_level']:
outlist.append(env['name'])
# Return as a JSON list
print(json.dumps(outlist))

View File

@@ -87,6 +87,30 @@
</screenshots>
<releases>
<release version="2.7.8" date="2025-08-30">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.8</url>
</release>
<release version="2.7.7" date="2025-08-28">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.7</url>
</release>
<release version="2.7.6" date="2025-08-12">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.6</url>
</release>
<release version="2.7.5" date="2025-08-09">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.5</url>
</release>
<release version="2.7.4" date="2025-07-19">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.4</url>
</release>
<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>
<release version="2.7.1" date="2025-06-27">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.1</url>
</release>
<release version="2.7.0" date="2025-06-20">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.0</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:")
@@ -131,3 +144,33 @@ for lb in env.GetLibBuilders():
if lb.name == "meshtastic-device-ui":
lb.env.Append(CPPDEFINES=[("APP_VERSION", verObj["long"])])
break
# Get the display resolution from macros
def get_display_resolution(build_flags):
# Check "DISPLAY_SIZE" to determine the screen resolution
for flag in build_flags:
if isinstance(flag, tuple) and flag[0] == "DISPLAY_SIZE":
screen_width, screen_height = map(int, flag[1].split("x"))
return screen_width, screen_height
print("No screen resolution defined in build_flags. Please define DISPLAY_SIZE.")
exit(1)
def load_boot_logo(source, target, env):
build_flags = env.get("CPPDEFINES", [])
logo_w, logo_h = get_display_resolution(build_flags)
print(f"TFT build with {logo_w}x{logo_h} resolution detected")
# Load the boot logo from `branding/logo_<width>x<height>.png` if it exists
source_path = join(env["PROJECT_DIR"], "branding", f"logo_{logo_w}x{logo_h}.png")
dest_dir = join(env["PROJECT_DIR"], "data", "boot")
dest_path = join(dest_dir, "logo.png")
if env.File(source_path).exists():
print(f"Loading boot logo from {source_path}")
# Prepare the destination
env.Execute(f"mkdir -p {dest_dir} && rm -f {dest_path}")
# Copy the logo to the `data/boot` directory
env.Execute(f"cp {source_path} {dest_path}")
# Load the boot logo on TFT builds
if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
env.AddPreAction('$BUILD_DIR/littlefs.bin', load_boot_logo)

View File

@@ -1 +1 @@
2.5.3
2.6.4

View File

@@ -10,7 +10,8 @@
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
["0x239A", "0x002A"]
["0x239A", "0x002A"],
["0x2886", "0x1667"]
],
"usb_product": "HT-n5262",
"mcu": "nrf52840",

View File

@@ -0,0 +1,54 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x239A", "0x0071"]
],
"usb_product": "HT-n5262",
"mcu": "nrf52840",
"variant": "heltec_mesh_solar",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
"frameworks": ["arduino"],
"name": "Heltec nrf (Adafruit BSP)",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://heltec.org/project/meshsolar/",
"vendor": "Heltec"
}

52
boards/meshtiny.json Normal file
View File

@@ -0,0 +1,52 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_FEATHER -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x239A", "0x802A"]
],
"usb_product": "MeshTiny",
"mcu": "nrf52840",
"variant": "meshtiny",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
"frameworks": ["arduino", "freertos"],
"name": "MeshTiny",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://github.com/meshtastic/firmware",
"vendor": "MTools Tec"
}

View File

@@ -7,7 +7,10 @@
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_MDBT50Q_RX -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [["0x2886", "0x1668"]],
"hwids": [
["0x2886", "0x1668"],
["0x2886", "0x1667"]
],
"usb_product": "TRACKER L1",
"mcu": "nrf52840",
"variant": "seeed_wio_tracker_L1",

43
boards/t-deck-pro.json Normal file
View File

@@ -0,0 +1,43 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"memory_type": "qio_qspi",
"partitions": "default_16MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "esp32s3"
},
"connectivity": ["wifi", "bluetooth", "lora"],
"debug": {
"default_tool": "esp-builtin",
"onboard_tools": ["esp-builtin"],
"openocd_target": "esp32s3.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "LilyGo T-Deck Pro S3 (16M Flash 8M QSPI PSRAM )",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"require_upload_port": true,
"speed": 921600
},
"monitor": {
"speed": 115200
},
"url": "https://lilygo.cc/products/t-deck-pro",
"vendor": "LilyGo"
}

View File

@@ -11,7 +11,8 @@
["0x239A", "0x8029"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x239A", "0x802A"]
["0x239A", "0x802A"],
["0x2886", "0x0057"]
],
"usb_product": "T1000-E-BOOT",
"mcu": "nrf52840",

View File

@@ -5,7 +5,7 @@
},
"core": "stm32",
"cpu": "cortex-m4",
"extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_GENERIC_WLE5CCUX",
"extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_RAK3172_MODULE",
"f_cpu": "48000000L",
"mcu": "stm32wle5ccu",
"variant": "STM32WLxx/WL54CCU_WL55CCU_WLE4C(8-B-C)U_WLE5C(8-B-C)U",

View File

@@ -0,0 +1,41 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"memory_type": "qio_opi",
"partitions": "default_16MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DRAK3312",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1",
"-DBOARD_HAS_PSRAM"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "dio",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "rak3312"
},
"connectivity": ["wifi", "bluetooth"],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "WisCore RAK3312 Board",
"upload": {
"flash_size": "16MB",
"maximum_ram_size": 327680,
"maximum_size": 16777216,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 921600
},
"url": "https://www.rakwireless.com/en-us",
"vendor": "rakwireless"
}

17
branding/README.md Normal file
View File

@@ -0,0 +1,17 @@
# Meshtastic Branding / Whitelabeling
This directory is consumed during the creation of **event** firmware.
`bin/platformio-custom.py` determines the display resolution, and locates the corresponding `logo_<width>x<height>.png`.
Ex:
- `logo_800x480.png`
- `logo_480x480.png`
- `logo_480x320.png`
- `logo_320x480.png`
- `logo_320x240.png`
This file is copied to `data/boot/logo.png` before filesytem image compilation.
For additional examples see the [`event/defcon33` branch](https://github.com/meshtastic/firmware/tree/event/defcon33).

26
debian/changelog vendored
View File

@@ -1,4 +1,4 @@
meshtasticd (2.7.0.0) UNRELEASED; urgency=medium
meshtasticd (2.7.8.0) UNRELEASED; urgency=medium
[ Austin Lane ]
* Initial packaging
@@ -22,4 +22,26 @@ meshtasticd (2.7.0.0) UNRELEASED; urgency=medium
[ ]
* GitHub Actions Automatic version bump
-- <github-actions[bot]@users.noreply.github.com> Mon, 16 Jun 2025 02:10:49 +0000
[ ]
* GitHub Actions Automatic version bump
[ ]
* GitHub Actions Automatic version bump
[ Ubuntu ]
* GitHub Actions Automatic version bump
[ ]
* GitHub Actions Automatic version bump
[ ]
* GitHub Actions Automatic version bump
[ ]
* GitHub Actions Automatic version bump
* GitHub Actions Automatic version bump
[ ]
* GitHub Actions Automatic version bump
-- <github-actions[bot]@users.noreply.github.com> Sat, 30 Aug 2025 00:26:04 +0000

View File

@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# postinst script for meshtasticd
#
# see: dh_installdeb(1)

View File

@@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# postrm script for meshtasticd
#
# see: dh_installdeb(1)

View File

@@ -6,7 +6,8 @@ default_envs = tbeam
extra_configs =
arch/*/*.ini
variants/*/platformio.ini
variants/*/*/platformio.ini
variants/*/diy/*/platformio.ini
src/graphics/niche/InkHUD/PlatformioConfig.ini
description = Meshtastic
@@ -49,18 +50,19 @@ build_flags = -Wno-missing-field-initializers
-DMESHTASTIC_EXCLUDE_DROPZONE=1
-DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1
-DMESHTASTIC_EXCLUDE_HEALTH_TELEMETRY=1
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
-DMESHTASTIC_EXCLUDE_POWERSTRESS=1 ; exclude power stress test module from main firmware
-DMESHTASTIC_EXCLUDE_GENERIC_THREAD_MODULE=1
#-DBUILD_EPOCH=$UNIX_TIME
#-D OLED_PL=1
-D MAX_THREADS=40 ; As we've split modules, we have more threads to manage
#-DBUILD_EPOCH=$UNIX_TIME
#-D OLED_PL=1
monitor_speed = 115200
monitor_filters = direct
lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0119501e9983bd894830b02f545c377ee08d66fe.zip
# renovate: datasource=custom.pio depName=OneButton packageName=mathertel/library/OneButton
mathertel/OneButton@2.6.1
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/9573abb64dc9c94f3051348f2bf4fc5cedf03c22.zip
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
https://github.com/meshtastic/arduino-fsm/archive/7db3702bf0cfe97b783d6c72595e3f38e0b19159.zip
# renovate: datasource=git-refs depName=meshtastic-TinyGPSPlus packageName=https://github.com/meshtastic/TinyGPSPlus gitBranch=master
@@ -100,21 +102,29 @@ lib_deps =
# renovate: datasource=custom.pio depName=Syslog packageName=arcao/library/Syslog
arcao/Syslog@2.0.0
; Minimal networking libs for nrf52 (excludes Syslog to save flash)
[nrf52_networking_base]
lib_deps =
# renovate: datasource=custom.pio depName=TBPubSubClient packageName=thingsboard/library/TBPubSubClient
thingsboard/TBPubSubClient@2.12.1
# renovate: datasource=custom.pio depName=NTPClient packageName=arduino-libraries/library/NTPClient
arduino-libraries/NTPClient@3.2.1
[radiolib_base]
lib_deps =
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
jgromes/RadioLib@7.1.2
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/d99edaf43775c9b235aab20521b034c99e04e4a8.zip
https://github.com/meshtastic/device-ui/archive/8019704395b7539600d581330499208edcd80804.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
@@ -128,7 +138,7 @@ lib_deps =
# renovate: datasource=custom.pio depName=Adafruit MCP9808 packageName=adafruit/library/Adafruit MCP9808 Library
adafruit/Adafruit MCP9808 Library@2.0.2
# renovate: datasource=custom.pio depName=Adafruit INA260 packageName=adafruit/library/Adafruit INA260 Library
adafruit/Adafruit INA260 Library@1.5.2
adafruit/Adafruit INA260 Library@1.5.3
# renovate: datasource=custom.pio depName=Adafruit INA219 packageName=adafruit/library/Adafruit INA219
adafruit/Adafruit INA219@1.2.3
# renovate: datasource=custom.pio depName=Adafruit PM25 AQI Sensor packageName=adafruit/library/Adafruit PM25 AQI Sensor
@@ -167,8 +177,8 @@ lib_deps =
adafruit/Adafruit PCT2075@1.0.5
# renovate: datasource=custom.pio depName=DFRobot_BMM150 packageName=dfrobot/library/DFRobot_BMM150
dfrobot/DFRobot_BMM150@1.0.0
; (not included in native / portduino)
; (not included in native / portduino)
[environmental_extra]
lib_deps =
# renovate: datasource=custom.pio depName=Adafruit BMP3XX packageName=adafruit/library/Adafruit BMP3XX Library
@@ -176,7 +186,7 @@ lib_deps =
# renovate: datasource=custom.pio depName=Adafruit MAX1704X packageName=adafruit/library/Adafruit MAX1704X
adafruit/Adafruit MAX1704X@1.0.3
# renovate: datasource=custom.pio depName=Adafruit SHTC3 packageName=adafruit/library/Adafruit SHTC3 Library
adafruit/Adafruit SHTC3 Library@1.0.1
adafruit/Adafruit SHTC3 Library@1.0.2
# renovate: datasource=custom.pio depName=Adafruit LPS2X packageName=adafruit/library/Adafruit LPS2X
adafruit/Adafruit LPS2X@2.0.6
# renovate: datasource=custom.pio depName=Adafruit SHT31 packageName=adafruit/library/Adafruit SHT31 Library
@@ -194,4 +204,8 @@ lib_deps =
# renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
boschsensortec/BME68x Sensor Library@1.3.40408
# renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
# renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
sensirion/Sensirion Core@0.7.1
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
sensirion/Sensirion I2C SCD4x@1.1.0

View File

@@ -89,14 +89,22 @@ class BluetoothStatus : public Status
case ConnectionState::CONNECTED:
LOG_DEBUG("BluetoothStatus CONNECTED");
#ifdef BLE_LED
#ifdef BLE_LED_INVERTED
digitalWrite(BLE_LED, LOW);
#else
digitalWrite(BLE_LED, HIGH);
#endif
#endif
break;
case ConnectionState::DISCONNECTED:
LOG_DEBUG("BluetoothStatus DISCONNECTED");
#ifdef BLE_LED
#ifdef BLE_LED_INVERTED
digitalWrite(BLE_LED, HIGH);
#else
digitalWrite(BLE_LED, LOW);
#endif
#endif
break;
}

View File

@@ -1,7 +1,14 @@
#include "DisplayFormatters.h"
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName)
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
bool usePreset)
{
// If use_preset is false, always return "Custom"
if (!usePreset) {
return "Custom";
}
switch (preset) {
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
return useShortName ? "ShortT" : "ShortTurbo";

View File

@@ -4,5 +4,6 @@
class DisplayFormatters
{
public:
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName);
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
bool usePreset);
};

View File

@@ -20,6 +20,11 @@
#include "meshUtils.h"
#include "sleep.h"
#if defined(ARCH_PORTDUINO)
#include "api/WiFiServerAPI.h"
#include "input/LinuxInputImpl.h"
#endif
// Working USB detection for powered/charging states on the RAK platform
#ifdef NRF_APM
#include "nrfx_power.h"
@@ -103,7 +108,7 @@ NullSensor ina3221Sensor;
#endif
#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_STM32WL)
#if !MESHTASTIC_EXCLUDE_I2C
#include "modules/Telemetry/Sensor/MAX17048Sensor.h"
#include <utility>
extern std::pair<uint8_t, TwoWire *> nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1];
@@ -120,6 +125,16 @@ NullSensor max17048Sensor;
RAK9154Sensor rak9154Sensor;
#endif
#ifdef HAS_PPM
// note: XPOWERS_CHIP_XXX must be defined in variant.h
#include <XPowersLib.h>
XPowersPPM *PPM = NULL;
#endif
#ifdef HAS_BQ27220
#include "bq27220.h"
#endif
#ifdef HAS_PMU
XPowersLibInterface *PMU = NULL;
#else
@@ -278,7 +293,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
#endif
#if HAS_TELEMETRY && !defined(ARCH_STM32WL) && !defined(HAS_PMU) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#if HAS_TELEMETRY && !defined(HAS_PMU) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
if (hasINA()) {
return getINAVoltage();
}
@@ -456,8 +471,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
#ifdef EXT_CHRG_DETECT
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
#else
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_STM32WL) && \
!defined(DISABLE_INA_CHARGING_DETECTION)
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
if (hasINA()) {
// get current flow from INA sensor - negative value means power flowing into the battery
// default assuming BATTERY+ <--> INA_VIN+ <--> SHUNT RESISTOR <--> INA_VIN- <--> LOAD
@@ -503,7 +517,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
#endif
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_STM32WL)
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
uint16_t getINAVoltage()
{
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
@@ -666,6 +680,10 @@ bool Power::setup()
found = true;
} else if (lipoInit()) {
found = true;
} else if (lipoChargerInit()) {
found = true;
} else if (meshSolarInit()) {
found = true;
} else if (analogInit()) {
found = true;
}
@@ -680,9 +698,65 @@ bool Power::setup()
return found;
}
void Power::powerCommandsCheck()
{
if (rebootAtMsec && millis() > rebootAtMsec) {
LOG_INFO("Rebooting");
reboot();
}
if (shutdownAtMsec && millis() > shutdownAtMsec) {
shutdownAtMsec = 0;
shutdown();
}
}
void Power::reboot()
{
notifyReboot.notifyObservers(NULL);
#if defined(ARCH_ESP32)
ESP.restart();
#elif defined(ARCH_NRF52)
NVIC_SystemReset();
#elif defined(ARCH_RP2040)
rp2040.reboot();
#elif defined(ARCH_PORTDUINO)
deInitApiServer();
if (aLinuxInputImpl)
aLinuxInputImpl->deInit();
SPI.end();
Wire.end();
Serial1.end();
if (screen) {
delete screen;
screen = nullptr;
}
LOG_DEBUG("final reboot!");
::reboot();
#elif defined(ARCH_STM32WL)
HAL_NVIC_SystemReset();
#else
rebootAtMsec = -1;
LOG_WARN("FIXME implement reboot for this platform. Note that some settings require a restart to be applied");
#endif
}
void Power::shutdown()
{
LOG_INFO("Shutting down");
#if HAS_SCREEN
if (screen) {
#ifdef T_DECK_PRO
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button
#else
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
#endif
}
#endif
#if !defined(ARCH_STM32WL)
playShutdownMelody();
#endif
nodeDB->saveToDisk();
#if defined(ARCH_NRF52) || defined(ARCH_ESP32) || defined(ARCH_RP2040)
#ifdef PIN_LED1
@@ -694,7 +768,11 @@ void Power::shutdown()
#ifdef PIN_LED3
ledOff(PIN_LED3);
#endif
doDeepSleep(DELAY_FOREVER, false, false);
doDeepSleep(DELAY_FOREVER, true, true);
#elif defined(ARCH_PORTDUINO)
exit(EXIT_SUCCESS);
#else
LOG_WARN("FIXME implement shutdown for this platform");
#endif
}
@@ -1161,7 +1239,7 @@ bool Power::axpChipInit()
#endif
}
#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
#if !MESHTASTIC_EXCLUDE_I2C && __has_include(<Adafruit_MAX1704X.h>)
/**
* Wrapper class for an I2C MAX17048 Lipo battery sensor.
@@ -1238,3 +1316,213 @@ bool Power::lipoInit()
return false;
}
#endif
#if defined(HAS_PPM) && HAS_PPM
/**
* Adapter class for BQ25896/BQ27220 Lipo battery charger.
*/
class LipoCharger : public HasBatteryLevel
{
private:
BQ27220 *bq = nullptr;
public:
/**
* Init the I2C BQ25896 Lipo battery charger
*/
bool runOnce()
{
if (PPM == nullptr) {
PPM = new XPowersPPM;
bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR);
if (result) {
LOG_INFO("PPM BQ25896 init succeeded");
// Set the minimum operating voltage. Below this voltage, the PPM will protect
// PPM->setSysPowerDownVoltage(3100);
// Set input current limit, default is 500mA
// PPM->setInputCurrentLimit(800);
// Disable current limit pin
// PPM->disableCurrentLimitPin();
// Set the charging target voltage, Range:3840 ~ 4608mV ,step:16 mV
PPM->setChargeTargetVoltage(4288);
// Set the precharge current , Range: 64mA ~ 1024mA ,step:64mA
// PPM->setPrechargeCurr(64);
// The premise is that limit pin is disabled, or it will
// only follow the maximum charging current set by limit pin.
// Set the charging current , Range:0~5056mA ,step:64mA
PPM->setChargerConstantCurr(1024);
// To obtain voltage data, the ADC must be enabled first
PPM->enableMeasure();
// Turn on charging function
// If there is no battery connected, do not turn on the charging function
PPM->enableCharge();
} else {
LOG_WARN("PPM BQ25896 init failed");
delete PPM;
PPM = nullptr;
return false;
}
}
if (bq == nullptr) {
bq = new BQ27220;
bq->setDefaultCapacity(BQ27220_DESIGN_CAPACITY);
bool result = bq->init();
if (result) {
LOG_DEBUG("BQ27220 design capacity: %d", bq->getDesignCapacity());
LOG_DEBUG("BQ27220 fullCharge capacity: %d", bq->getFullChargeCapacity());
LOG_DEBUG("BQ27220 remaining capacity: %d", bq->getRemainingCapacity());
return true;
} else {
LOG_WARN("BQ27220 init failed");
delete bq;
bq = nullptr;
return false;
}
}
return false;
}
/**
* Battery state of charge, from 0 to 100 or -1 for unknown
*/
virtual int getBatteryPercent() override
{
return -1;
// return bq->getChargePercent(); // don't use BQ27220 for battery percent, it is not calibrated
}
/**
* The raw voltage of the battery in millivolts, or NAN if unknown
*/
virtual uint16_t getBattVoltage() override { return bq->getVoltage(); }
/**
* return true if there is a battery installed in this unit
*/
virtual bool isBatteryConnect() override { return PPM->getBattVoltage() > 0; }
/**
* return true if there is an external power source detected
*/
virtual bool isVbusIn() override { return PPM->getVbusVoltage() > 0; }
/**
* return true if the battery is currently charging
*/
virtual bool isCharging() override
{
bool isCharging = PPM->isCharging();
if (isCharging) {
LOG_DEBUG("BQ27220 time to full charge: %d min", bq->getTimeToFull());
} else {
if (!PPM->isVbusIn()) {
LOG_DEBUG("BQ27220 time to empty: %d min (%d mAh)", bq->getTimeToEmpty(), bq->getRemainingCapacity());
}
}
return isCharging;
}
};
LipoCharger lipoCharger;
/**
* Init the Lipo battery charger
*/
bool Power::lipoChargerInit()
{
bool result = lipoCharger.runOnce();
LOG_DEBUG("Power::lipoChargerInit lipo sensor is %s", result ? "ready" : "not ready yet");
if (!result)
return false;
batteryLevel = &lipoCharger;
return true;
}
#else
/**
* The Lipo battery level sensor is unavailable - default to AnalogBatteryLevel
*/
bool Power::lipoChargerInit()
{
return false;
}
#endif
#ifdef HELTEC_MESH_SOLAR
#include "meshSolarApp.h"
/**
* meshSolar class for an SMBUS battery sensor.
*/
class meshSolarBatteryLevel : public HasBatteryLevel
{
public:
/**
* Init the I2C meshSolar battery level sensor
*/
bool runOnce()
{
meshSolarStart();
return true;
}
/**
* Battery state of charge, from 0 to 100 or -1 for unknown
*/
virtual int getBatteryPercent() override { return meshSolarGetBatteryPercent(); }
/**
* The raw voltage of the battery in millivolts, or NAN if unknown
*/
virtual uint16_t getBattVoltage() override { return meshSolarGetBattVoltage(); }
/**
* return true if there is a battery installed in this unit
*/
virtual bool isBatteryConnect() override { return meshSolarIsBatteryConnect(); }
/**
* return true if there is an external power source detected
*/
virtual bool isVbusIn() override { return meshSolarIsVbusIn(); }
/**
* return true if the battery is currently charging
*/
virtual bool isCharging() override { return meshSolarIsCharging(); }
};
meshSolarBatteryLevel meshSolarLevel;
/**
* Init the meshSolar battery level sensor
*/
bool Power::meshSolarInit()
{
bool result = meshSolarLevel.runOnce();
LOG_DEBUG("Power::meshSolarInit mesh solar sensor is %s", result ? "ready" : "not ready yet");
if (!result)
return false;
batteryLevel = &meshSolarLevel;
return true;
}
#else
/**
* The meshSolar battery level sensor is unavailable - default to AnalogBatteryLevel
*/
bool Power::meshSolarInit()
{
return false;
}
#endif

View File

@@ -26,7 +26,7 @@
#ifndef SLEEP_TIME
#define SLEEP_TIME 30
#endif
#if EXCLUDE_POWER_FSM
#if MESHTASTIC_EXCLUDE_POWER_FSM
FakeFsm powerFSM;
void PowerFSM_setup(){};
#else
@@ -72,7 +72,7 @@ extern Power *power;
static void shutdownEnter()
{
LOG_DEBUG("State: SHUTDOWN");
power->shutdown();
shutdownAtMsec = millis();
}
#include "error.h"

View File

@@ -22,7 +22,7 @@
#define EVENT_SHUTDOWN 16 // force a full shutdown now (not just sleep)
#define EVENT_INPUT 17 // input broker wants something, we need to wake up and enable screen
#if EXCLUDE_POWER_FSM
#if MESHTASTIC_EXCLUDE_POWER_FSM
class FakeFsm
{
public:

View File

@@ -18,7 +18,7 @@ class PowerFSMThread : public OSThread
protected:
int32_t runOnce() override
{
#if !EXCLUDE_POWER_FSM
#if !MESHTASTIC_EXCLUDE_POWER_FSM
powerFSM.run_machine();
/// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake

View File

@@ -352,8 +352,8 @@ void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16
for (uint16_t i = 0; i < len; i += 16) {
if (i % 128 == 0)
log(logLevel, " +------------------------------------------------+ +----------------+");
char s[] = "| | | |\n";
uint8_t ix = 1, iy = 52;
char s[] = " | | | |\n";
uint8_t ix = 5, iy = 56;
for (uint8_t j = 0; j < 16; j++) {
if (i + j < len) {
uint8_t c = buf[i + j];
@@ -367,10 +367,8 @@ void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16
}
}
uint8_t index = i / 16;
if (i < 256)
log(logLevel, " ");
log(logLevel, "%02x", index);
log(logLevel, ".");
sprintf(s, "%03x", index);
s[3] = '.';
log(logLevel, s);
}
log(logLevel, " +------------------------------------------------+ +----------------+");
@@ -393,4 +391,4 @@ std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...)
break;
}
return std::string(formatted.get());
}
}

View File

@@ -64,6 +64,14 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con
int32_t SerialConsole::runOnce()
{
#ifdef HELTEC_MESH_SOLAR
//After enabling the mesh solar serial port module configuration, command processing is handled by the serial port module.
if(moduleConfig.serial.enabled && moduleConfig.serial.override_console_serial_port
&& moduleConfig.serial.mode==meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MS_CONFIG)
{
return 250;
}
#endif
return runOncePart();
}

View File

@@ -47,10 +47,6 @@ int BuzzerFeedbackThread::handleInputEvent(const InputEvent *event)
playComboTune(); // Ping sent feedback
break;
case INPUT_BROKER_SHUTDOWN:
playShutdownMelody(); // Shutdown feedback
break;
default:
// For other events, check if it's a printable character
if (event->kbchar >= 32 && event->kbchar <= 126) {
@@ -69,10 +65,7 @@ int32_t BuzzerFeedbackThread::runOnce()
// This thread is primarily event-driven, but we can use runOnce
// for any periodic tasks if needed in the future
if (needsUpdate) {
needsUpdate = false;
// Could add any periodic processing here
}
needsUpdate = false;
// Run every 100ms when active, less frequently when idle
return needsUpdate ? 100 : 1000;

View File

@@ -140,6 +140,10 @@ bool playNextLeadUpNote()
playTones(&note, 1); // Play single note using existing playTones function
leadUpNoteIndex++;
if (leadUpNoteIndex >= leadUpNotesCount) {
return false; // this was the final note
}
return true; // Note was played (playTones handles buzzer availability internally)
}

View File

@@ -13,5 +13,6 @@ enum class Cmd {
START_FIRMWARE_UPDATE_SCREEN,
STOP_BOOT_SCREEN,
SHOW_PREV_FRAME,
SHOW_NEXT_FRAME
SHOW_NEXT_FRAME,
NOOP
};

View File

@@ -26,10 +26,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <Arduino.h>
#ifdef RV3028_RTC
#if __has_include("Melopero_RV3028.h")
#include "Melopero_RV3028.h"
#endif
#ifdef PCF8563_RTC
#if __has_include("pcf8563.h")
#include "pcf8563.h"
#endif
@@ -135,7 +135,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// -----------------------------------------------------------------------------
// OLED & Input
// -----------------------------------------------------------------------------
#if defined(SEEED_WIO_TRACKER_L1)
#if defined(SEEED_WIO_TRACKER_L1) && !defined(SEEED_WIO_TRACKER_L1_EINK)
#define SSD1306_ADDRESS 0x3D
#define USE_SH1106
#else
@@ -150,11 +150,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Define if screen should be mirrored left to right
// #define SCREEN_MIRROR
// I2C Keyboards (M5Stack, RAK14004, T-Deck)
// I2C Keyboards (M5Stack, RAK14004, T-Deck, T-Deck Pro, T-Lora Pager, CardKB, BBQ10, MPR121, TCA8418)
#define CARDKB_ADDR 0x5F
#define TDECK_KB_ADDR 0x55
#define BBQ10_KB_ADDR 0x1F
#define MPR121_KB_ADDR 0x5A
#define TCA8418_KB_ADDR 0x34
// -----------------------------------------------------------------------------
// SENSOR
@@ -189,11 +190,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define DFROBOT_RAIN_ADDR 0x1d
#define NAU7802_ADDR 0x2A
#define MAX30102_ADDR 0x57
#define SCD4X_ADDR 0x62
#define MLX90614_ADDR_DEF 0x5A
#define CGRADSENS_ADDR 0x66
#define LTR390UV_ADDR 0x53
#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418
#define XPOWERS_AXP192_AXP2101_ADDRESS 0x34 // same adress as TCA8418_KB
#define PCT2075_ADDR 0x37
#define BQ27220_ADDR 0x55 // same address as TDECK_KB
#define BQ25896_ADDR 0x6B
#define LTR553ALS_ADDR 0x23
// -----------------------------------------------------------------------------
// ACCELEROMETER
@@ -207,6 +212,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define BMX160_ADDR 0x69
#define ICM20948_ADDR 0x69
#define ICM20948_ADDR_ALT 0x68
#define BHI260AP_ADDR 0x28
#define BMM150_ADDR 0x13
// -----------------------------------------------------------------------------
@@ -229,6 +235,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Touchscreen
// -----------------------------------------------------------------------------
#define FT6336U_ADDR 0x48
#define CST328_ADDR 0x1A
// -----------------------------------------------------------------------------
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)

View File

@@ -41,6 +41,12 @@ ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
return firstOfOrNONE(9, types);
}
ScanI2C::FoundDevice ScanI2C::firstAQI() const
{
ScanI2C::DeviceType types[] = {PMSA0031, SCD4X};
return firstOfOrNONE(2, types);
}
ScanI2C::FoundDevice ScanI2C::firstRGBLED() const
{
ScanI2C::DeviceType types[] = {NCP5623, LP5562};
@@ -80,4 +86,4 @@ bool ScanI2C::DeviceAddress::operator<(const ScanI2C::DeviceAddress &other) cons
|| (port != NO_I2C && other.port != NO_I2C && (address < other.address));
}
ScanI2C::FoundDevice::FoundDevice(ScanI2C::DeviceType type, ScanI2C::DeviceAddress address) : type(type), address(address) {}
ScanI2C::FoundDevice::FoundDevice(ScanI2C::DeviceType type, ScanI2C::DeviceAddress address) : type(type), address(address) {}

View File

@@ -61,6 +61,7 @@ class ScanI2C
FT6336U,
STK8BAXX,
ICM20948,
SCD4X,
MAX30102,
TPS65233,
MPR121KB,
@@ -73,7 +74,13 @@ class ScanI2C
RAK12035,
TCA8418KB,
PCT2075,
CST328,
BQ25896,
BQ27220,
LTR553ALS,
BHI260AP,
BMM150,
DRV2605
} DeviceType;
// typedef uint8_t DeviceAddress;
@@ -126,6 +133,8 @@ class ScanI2C
FoundDevice firstAccelerometer() const;
FoundDevice firstAQI() const;
FoundDevice firstRGBLED() const;
virtual FoundDevice find(DeviceType) const;

View File

@@ -184,8 +184,13 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
type = RTC_RV3028;
logFoundDevice("RV3028", (uint8_t)addr.address);
rtc.initI2C(*i2cBus);
rtc.writeToRegister(0x35, 0x07); // no Clkout
rtc.writeToRegister(0x37, 0xB4);
// Update RTC EEPROM settings, if necessary
if (rtc.readEEPROMRegister(0x35) != 0x07) {
rtc.writeEEPROMRegister(0x35, 0x07); // no Clkout
}
if (rtc.readEEPROMRegister(0x37) != 0xB4) {
rtc.writeEEPROMRegister(0x37, 0xB4);
}
break;
#endif
@@ -206,7 +211,17 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
}
break;
SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard", (uint8_t)addr.address);
case TDECK_KB_ADDR:
// Do we have the T-Deck keyboard or the T-Deck Pro battery sensor?
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x04), 1);
if (registerValue != 0) {
logFoundDevice("BQ27220", (uint8_t)addr.address);
type = BQ27220;
} else {
logFoundDevice("TDECKKB", (uint8_t)addr.address);
type = TDECKKB;
}
break;
SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "ST7567", (uint8_t)addr.address);
@@ -396,6 +411,12 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
logFoundDevice("BQ24295", (uint8_t)addr.address);
break;
}
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x14), 1); // get ID
if ((registerValue & 0b00000011) == 0b00000010) {
type = BQ25896;
logFoundDevice("BQ25896", (uint8_t)addr.address);
break;
}
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0F), 1); // get ID
if (registerValue == 0x6A) {
type = LSM6DS3;
@@ -447,6 +468,10 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
SCAN_SIMPLE_CASE(DFROBOT_RAIN_ADDR, DFROBOT_RAIN, "DFRobot Rain Gauge", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(CST328_ADDR, CST328, "CST328", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(LTR553ALS_ADDR, LTR553ALS, "LTR553ALS", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(BHI260AP_ADDR, BHI260AP, "BHI260AP", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(SCD4X_ADDR, SCD4X, "SCD4X", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(BMM150_ADDR, BMM150, "BMM150", (uint8_t)addr.address);
#ifdef HAS_TPS65233
SCAN_SIMPLE_CASE(TPS65233_ADDR, TPS65233, "TPS65233", (uint8_t)addr.address);
@@ -458,8 +483,14 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
type = MLX90614;
logFoundDevice("MLX90614", (uint8_t)addr.address);
} else {
type = MPR121KB;
logFoundDevice("MPR121KB", (uint8_t)addr.address);
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); // DRV2605_REG_STATUS
if (registerValue == 0xe0) {
type = DRV2605;
logFoundDevice("DRV2605", (uint8_t)addr.address);
} else {
type = MPR121KB;
logFoundDevice("MPR121KB", (uint8_t)addr.address);
}
}
break;
@@ -556,4 +587,4 @@ void ScanI2CTwoWire::logFoundDevice(const char *device, uint8_t address)
{
LOG_INFO("%s found at address 0x%x", device, address);
}
#endif
#endif

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
@@ -643,8 +643,16 @@ bool GPS::setup()
delay(250);
} else if (IS_ONE_OF(gnssModel, GNSS_MODEL_AG3335, GNSS_MODEL_AG3352)) {
_serial_gps->write("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC
if (config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_IN ||
config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_NP_865) {
_serial_gps->write("$PAIR066,1,0,1,0,0,1*3B\r\n"); // Enable GPS+GALILEO+NAVIC
// GPS GLONASS GALILEO BDS QZSS NAVIC
// 1 0 1 0 0 1
} else {
_serial_gps->write("$PAIR066,1,1,1,1,0,0*3A\r\n"); // Enable GPS+GLONASS+GALILEO+BDS
// GPS GLONASS GALILEO BDS QZSS NAVIC
// 1 1 1 1 0 0
}
// Configure NMEA (sentences will output once per fix)
_serial_gps->write("$PAIR062,0,1*3F\r\n"); // GGA ON
_serial_gps->write("$PAIR062,1,0*3F\r\n"); // GLL OFF
@@ -835,9 +843,6 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
setPowerPMU(true); // Power (PMU): on
writePinStandby(false); // Standby (pin): awake (not standby)
setPowerUBLOX(true); // Standby (UBLOX): awake
#ifdef GNSS_AIROHA
lastFixStartMsec = 0;
#endif
break;
case GPS_SOFTSLEEP:
@@ -855,9 +860,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
writePinStandby(true); // Standby (pin): asleep (not awake)
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
#ifdef GNSS_AIROHA
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
digitalWrite(PIN_GPS_EN, LOW);
}
digitalWrite(PIN_GPS_EN, LOW);
#endif
break;
@@ -869,9 +872,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
writePinStandby(true); // Standby (pin): asleep
setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely
#ifdef GNSS_AIROHA
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
digitalWrite(PIN_GPS_EN, LOW);
}
digitalWrite(PIN_GPS_EN, LOW);
#endif
break;
}
@@ -1054,6 +1055,8 @@ void GPS::down()
}
// If update interval long enough (or softsleep unsupported): hardsleep instead
setPowerState(GPS_HARDSLEEP, sleepTime);
// Reset the fix quality to 0, since we're off.
fixQual = 0;
}
}
@@ -1113,11 +1116,19 @@ int32_t GPS::runOnce()
shouldPublish = true;
}
uint8_t prev_fixQual = fixQual;
bool gotLoc = lookForLocation();
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
LOG_DEBUG("hasValidLocation RISING EDGE");
hasValidLocation = true;
shouldPublish = true;
// Hold for 20secs after getting a lock to download ephemeris etc
fixHoldEnds = millis() + 20000;
}
if (gotLoc && prev_fixQual == 0) { // just got a lock after turning back on.
fixHoldEnds = millis() + 20000;
shouldPublish = true; // Publish immediately, since next publish is at end of hold
}
bool tooLong = scheduling.searchedTooLong();
@@ -1126,8 +1137,7 @@ int32_t GPS::runOnce()
// Once we get a location we no longer desperately want an update
if ((gotLoc && gotTime) || tooLong) {
if (tooLong) {
if (tooLong && !gotLoc) {
// we didn't get a location during this ack window, therefore declare loss of lock
if (hasValidLocation) {
LOG_DEBUG("hasValidLocation FALLING EDGE");
@@ -1135,9 +1145,15 @@ int32_t GPS::runOnce()
p = meshtastic_Position_init_default;
hasValidLocation = false;
}
down();
shouldPublish = true; // publish our update for this just finished acquisition window
if (millis() > fixHoldEnds) {
shouldPublish = true; // publish our update at the end of the lock hold
publishUpdate();
down();
#ifdef GPS_DEBUG
} else {
LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view);
#endif
}
}
// If state has changed do a publish
@@ -1496,28 +1512,10 @@ static int32_t toDegInt(RawDegrees d)
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a new location
* @return true if we've set a new time
*/
bool GPS::lookForTime()
{
#ifdef GNSS_AIROHA
uint8_t fix = reader.fixQuality();
if (fix > 0) {
if (lastFixStartMsec > 0) {
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
return false;
} else {
clearBuffer();
}
} else {
lastFixStartMsec = millis();
return false;
}
} else {
return false;
}
#endif
auto ti = reader.time;
auto d = reader.date;
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
@@ -1536,8 +1534,12 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
if (t.tm_mon > -1) {
LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d age %d", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
t.tm_sec, ti.age());
perhapsSetRTC(RTCQualityGPS, t);
return true;
if (perhapsSetRTC(RTCQualityGPS, t) == RTCSetResultSuccess) {
LOG_DEBUG("Time set.");
return true;
} else {
return false;
}
} else
return false;
} else
@@ -1552,25 +1554,6 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
*/
bool GPS::lookForLocation()
{
#ifdef GNSS_AIROHA
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
uint8_t fix = reader.fixQuality();
if (fix > 0) {
if (lastFixStartMsec > 0) {
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
return false;
} else {
clearBuffer();
}
} else {
lastFixStartMsec = millis();
return false;
}
} else {
return false;
}
}
#endif
// By default, TinyGPS++ does not parse GPGSA lines, which give us
// the 2D/3D fixType (see NMEAGPS.h)
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)

View File

@@ -159,7 +159,7 @@ class GPS : private concurrency::OSThread
uint8_t fixType = 0; // fix type from GPGSA
#endif
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
uint32_t fixHoldEnds = 0;
uint32_t rx_gpio = 0;
uint32_t tx_gpio = 0;

View File

@@ -23,7 +23,7 @@ static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only upda
* Reads the current date and time from the RTC module and updates the system time.
* @return True if the RTC was successfully read and the system time was updated, false otherwise.
*/
void readFromRTC()
RTCSetResult readFromRTC()
{
struct timeval tv; /* btw settimeofday() is helpful here too*/
#ifdef RV3028_RTC
@@ -44,15 +44,23 @@ void readFromRTC()
t.tm_sec = rtc.getSecond();
tv.tv_sec = gm_mktime(&t);
tv.tv_usec = 0;
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("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
if (currentQuality == RTCQualityNone) {
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
currentQuality = RTCQualityDevice;
}
return RTCSetResultSuccess;
}
#elif defined(PCF8563_RTC)
if (rtc_found.address == PCF8563_RTC) {
@@ -75,15 +83,23 @@ void readFromRTC()
t.tm_sec = tc.second;
tv.tv_sec = gm_mktime(&t);
tv.tv_usec = 0;
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("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
if (currentQuality == RTCQualityNone) {
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
currentQuality = RTCQualityDevice;
}
return RTCSetResultSuccess;
}
#else
if (!gettimeofday(&tv, NULL)) {
@@ -92,8 +108,10 @@ void readFromRTC()
LOG_DEBUG("Read RTC time as %ld", printableEpoch);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
return RTCSetResultSuccess;
}
#endif
return RTCSetResultNotSet;
}
/**
@@ -101,11 +119,11 @@ void readFromRTC()
*
* @param q The quality of the provided time.
* @param tv A pointer to a timeval struct containing the time to potentially set the RTC to.
* @return True if the RTC was set, false otherwise.
* @return RTCSetResult
*
* If we haven't yet set our RTC this boot, set it from a GPS derived time
*/
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
{
static uint32_t lastSetMsec = 0;
uint32_t now = millis();
@@ -113,7 +131,11 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
#ifdef BUILD_EPOCH
if (tv->tv_sec < BUILD_EPOCH) {
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
return false;
return RTCSetResultInvalidTime;
} else if (tv->tv_sec > (BUILD_EPOCH + FORTY_YEARS)) {
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH,
BUILD_EPOCH + FORTY_YEARS);
return RTCSetResultInvalidTime;
}
#endif
@@ -184,9 +206,9 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
readFromRTC();
#endif
return true;
return RTCSetResultSuccess;
} else {
return false;
return RTCSetResultNotSet; // RTC was already set with a higher quality time
}
}
@@ -215,7 +237,7 @@ const char *RtcName(RTCQuality quality)
* @param t The time to potentially set the RTC to.
* @return True if the RTC was set to the provided time, false otherwise.
*/
bool perhapsSetRTC(RTCQuality q, struct tm &t)
RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
{
/* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
@@ -226,12 +248,23 @@ bool 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;
} else if (tv.tv_sec > (BUILD_EPOCH + FORTY_YEARS)) {
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch, BUILD_EPOCH,
BUILD_EPOCH + FORTY_YEARS);
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) {
// LOG_DEBUG("Ignore invalid GPS month=%d, year=%d, unixtime=%ld", t.tm_mon, t.tm_year, tv.tv_sec);
return false;
return RTCSetResultInvalidTime;
} else {
return perhapsSetRTC(q, &tv);
}
@@ -244,11 +277,15 @@ bool perhapsSetRTC(RTCQuality q, struct tm &t)
*/
int32_t getTZOffset()
{
#if MESHTASTIC_EXCLUDE_TZ
return 0;
#else
time_t now = getTime(false);
struct tm *gmt;
gmt = gmtime(&now);
gmt->tm_isdst = -1;
return (int32_t)difftime(now, mktime(gmt));
#endif
}
/**

View File

@@ -22,13 +22,22 @@ enum RTCQuality {
RTCQualityGPS = 4
};
/// The RTC set result codes
/// Used to indicate the result of an attempt to set the RTC.
enum RTCSetResult {
RTCSetResultNotSet = 0, ///< RTC was set successfully
RTCSetResultSuccess = 1, ///< RTC was set successfully
RTCSetResultInvalidTime = 3, ///< The provided time was invalid (e.g., before the build epoch)
RTCSetResultError = 4 ///< An error occurred while setting the RTC
};
RTCQuality getRTCQuality();
extern uint32_t lastSetFromPhoneNtpOrGps;
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false);
bool perhapsSetRTC(RTCQuality q, struct tm &t);
RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false);
RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t);
/// Return a string name for the quality
const char *RtcName(RTCQuality quality);
@@ -39,10 +48,13 @@ uint32_t getTime(bool local = false);
/// Return time since 1970 in secs. If quality is RTCQualityNone return zero
uint32_t getValidTime(RTCQuality minQuality, bool local = false);
void readFromRTC();
RTCSetResult readFromRTC();
time_t gm_mktime(struct tm *tm);
#define SEC_PER_DAY 86400
#define SEC_PER_HOUR 3600
#define SEC_PER_MIN 60
#ifdef BUILD_EPOCH
#define FORTY_YEARS (40UL * 365 * SEC_PER_DAY) // probably time to update your firmware
#endif

View File

@@ -6,6 +6,10 @@
#include "main.h"
#include <SPI.h>
#ifdef GXEPD2_DRIVER_0
#include "einkDetect.h"
#endif
/*
The macros EINK_DISPLAY_MODEL, EINK_WIDTH, and EINK_HEIGHT are defined as build_flags in a variant's platformio.ini
Previously, these macros were defined at the top of this file.
@@ -63,20 +67,28 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
const bool flipped = config.display.flip_screen;
// HACK for L1 EInk
#if defined(SEEED_WIO_TRACKER_L1_EINK)
// For SEEED_WIO_TRACKER_L1_EINK, setRotation(3) is correct but mirrored; flip both axes
for (uint32_t y = 0; y < displayHeight; y++) {
for (uint32_t x = 0; x < displayWidth; x++) {
auto b = buffer[x + (y / 8) * displayWidth];
auto isset = b & (1 << (y & 7));
adafruitDisplay->drawPixel((displayWidth - 1) - x, (displayHeight - 1) - y, isset ? GxEPD_BLACK : GxEPD_WHITE);
}
}
#else
for (uint32_t y = 0; y < displayHeight; y++) {
for (uint32_t x = 0; x < displayWidth; x++) {
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficient
auto b = buffer[x + (y / 8) * displayWidth];
auto isset = b & (1 << (y & 7));
// Handle flip here, rather than with setRotation(),
// Avoids issues when display width is not a multiple of 8
if (flipped)
adafruitDisplay->drawPixel((displayWidth - 1) - x, (displayHeight - 1) - y, isset ? GxEPD_BLACK : GxEPD_WHITE);
else
adafruitDisplay->drawPixel(x, y, isset ? GxEPD_BLACK : GxEPD_WHITE);
}
}
#endif
// Trigger the refresh in GxEPD2
LOG_DEBUG("Update E-Paper");
@@ -136,17 +148,32 @@ bool EInkDisplay::connect()
#endif
#endif
#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1)
#if defined(TTGO_T_ECHO) || defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE)
{
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init();
#ifdef ELECROW_ThinkNode_M1
#if defined(ELECROW_ThinkNode_M1) || defined(T_ECHO_LITE)
adafruitDisplay->setRotation(4);
#else
adafruitDisplay->setRotation(3);
#endif
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
}
#elif defined(ELECROW_ThinkNode_M5)
{
// Start HSPI
hspi = new SPIClass(HSPI);
hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init();
adafruitDisplay->setRotation(4);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
}
#elif defined(MESHLINK)
@@ -174,9 +201,8 @@ bool EInkDisplay::connect()
}
}
#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213) || \
defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || defined(CROWPANEL_ESP32S3_5_EPAPER) || \
defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER)
#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || \
defined(CROWPANEL_ESP32S3_5_EPAPER) || defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER)
{
// Start HSPI
hspi = new SPIClass(HSPI);
@@ -203,7 +229,7 @@ bool EInkDisplay::connect()
adafruitDisplay->setRotation(0);
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
}
#elif defined(M5_COREINK)
#elif defined(M5_COREINK) || defined(T_DECK_PRO)
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
@@ -217,7 +243,7 @@ bool EInkDisplay::connect()
adafruitDisplay->setRotation(1);
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
}
#elif defined(HELTEC_MESH_POCKET)
#elif defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK)
{
spi1 = &SPI1;
spi1->begin();
@@ -231,7 +257,25 @@ bool EInkDisplay::connect()
// Init GxEPD2
adafruitDisplay->init();
adafruitDisplay->setRotation(3);
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
}
#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213)
// Detect display model, before starting SPI
EInkDetectionResult displayModel = detectEInk();
// Start HSPI
hspi = new SPIClass(HSPI);
hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS
// Create GxEPD2 object
adafruitDisplay = new GxEPD2_Multi<GXEPD2_DRIVER_0, GXEPD2_DRIVER_1>((uint8_t)displayModel, PIN_EINK_CS, PIN_EINK_DC,
PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
// Init GxEPD2
adafruitDisplay->init();
adafruitDisplay->setRotation(3);
#endif
return true;

View File

@@ -5,6 +5,10 @@
#include "GxEPD2_BW.h"
#include <OLEDDisplay.h>
#ifdef GXEPD2_DRIVER_0 // If variant has multiple possible display models
#include "GxEPD2Multi.h"
#endif
/**
* An adapter class that allows using the GxEPD2 library as if it was an OLEDDisplay implementation.
*
@@ -63,17 +67,24 @@ class EInkDisplay : public OLEDDisplay
// Connect to the display
virtual bool connect() override;
// AdafruitGFX display object - instantiated in connect(), variant specific
#ifdef GXEPD2_DRIVER_0
// AdafruitGFX display object - wrapper for multiple drivers
// Allows runtime detection of multiple displays
// Avoid this situation if possible!
GxEPD2_Multi<GXEPD2_DRIVER_0, GXEPD2_DRIVER_1> *adafruitDisplay = NULL;
#else
// AdafruitGFX display object (for single display model) - instantiated in connect(), variant specific
GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT> *adafruitDisplay = NULL;
#endif
// If display uses HSPI
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \
defined(HELTEC_VISION_MASTER_E290) || defined(TLORA_T3S3_EPAPER) || defined(CROWPANEL_ESP32S3_5_EPAPER) || \
defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER)
defined(CROWPANEL_ESP32S3_4_EPAPER) || defined(CROWPANEL_ESP32S3_2_EPAPER) || defined(ELECROW_ThinkNode_M5)
SPIClass *hspi = NULL;
#endif
#if defined(HELTEC_MESH_POCKET)
#if defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK)
SPIClass *spi1 = NULL;
#endif

135
src/graphics/GxEPD2Multi.h Normal file
View File

@@ -0,0 +1,135 @@
// Wrapper class for GxEPD2_BW
// Generic signature at build-time, so that we can detect display model at run-time
// Workaround for issue of GxEPD2_BW objects not having a shared base class
// Only exposes methods which we are actually using
template <typename Driver0, typename Driver1> class GxEPD2_Multi
{
public:
void drawPixel(int16_t x, int16_t y, uint16_t color)
{
if (which == 0)
driver0->drawPixel(x, y, color);
else
driver1->drawPixel(x, y, color);
}
bool nextPage()
{
if (which == 0)
return driver0->nextPage();
else
return driver1->nextPage();
}
void hibernate()
{
if (which == 0)
driver0->hibernate();
else
driver1->hibernate();
}
void init(uint32_t serial_diag_bitrate = 0)
{
if (which == 0)
driver0->init(serial_diag_bitrate);
else
driver1->init(serial_diag_bitrate);
}
void init(uint32_t serial_diag_bitrate, bool initial, uint16_t reset_duration = 20, bool pulldown_rst_mode = false)
{
if (which == 0)
driver0->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode);
else
driver1->init(serial_diag_bitrate, initial, reset_duration, pulldown_rst_mode);
}
void setRotation(uint8_t x)
{
if (which == 0)
driver0->setRotation(x);
else
driver1->setRotation(x);
}
void setPartialWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
{
if (which == 0)
driver0->setPartialWindow(x, y, w, h);
else
driver1->setPartialWindow(x, y, w, h);
}
void setFullWindow()
{
if (which == 0)
driver0->setFullWindow();
else
driver1->setFullWindow();
}
int16_t width()
{
if (which == 0)
return driver0->width();
else
return driver1->width();
}
int16_t height()
{
if (which == 0)
return driver0->height();
else
return driver1->height();
}
void clearScreen(uint8_t value = 0xFF)
{
if (which == 0)
driver0->clearScreen();
else
driver1->clearScreen();
}
void endAsyncFull()
{
if (which == 0)
driver0->endAsyncFull();
else
driver1->endAsyncFull();
}
// Exposes methods of the GxEPD2_EPD object which is usually available as GxEPD2_BW::epd
class Epd2Wrapper
{
public:
bool isBusy() { return m_epd2->isBusy(); }
GxEPD2_EPD *m_epd2;
} epd2;
// Constructor
// Select driver by passing whichDriver as 0 or 1
GxEPD2_Multi(uint8_t whichDriver, int16_t cs, int16_t dc, int16_t rst, int16_t busy, SPIClass &spi)
{
assert(whichDriver == 0 || whichDriver == 1);
which = whichDriver;
LOG_DEBUG("GxEPD2_Multi driver: %d", which);
if (which == 0) {
driver0 = new GxEPD2_BW<Driver0, Driver0::HEIGHT>(Driver0(cs, dc, rst, busy, spi));
epd2.m_epd2 = &(driver0->epd2);
} else if (which == 1) {
driver1 = new GxEPD2_BW<Driver1, Driver1::HEIGHT>(Driver1(cs, dc, rst, busy, spi));
epd2.m_epd2 = &(driver1->epd2);
}
}
private:
uint8_t which;
GxEPD2_BW<Driver0, Driver0::HEIGHT> *driver0;
GxEPD2_BW<Driver1, Driver1::HEIGHT> *driver1;
};

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"
@@ -31,6 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "TimeFormatters.h"
#include "draw/ClockRenderer.h"
#include "draw/DebugRenderer.h"
#include "draw/MenuHandler.h"
#include "draw/MessageRenderer.h"
#include "draw/NodeListRenderer.h"
#include "draw/NotificationRenderer.h"
@@ -43,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"
@@ -58,7 +59,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mesh/Channels.h"
#include "mesh/generated/meshtastic/deviceonly.pb.h"
#include "meshUtils.h"
#include "modules/AdminModule.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
#include "modules/WaypointModule.h"
@@ -69,6 +69,8 @@ using graphics::Emote;
using graphics::emotes;
using graphics::numEmotes;
extern uint16_t TFT_MESH;
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
#include "mesh/wifi/WiFiAPClient.h"
#endif
@@ -135,21 +137,82 @@ extern bool hasUnreadMessage;
// Displays a temporary centered banner message (e.g., warning, status, etc.)
// The banner appears in the center of the screen and disappears after the specified duration
// Called to trigger a banner with custom message and duration
void Screen::showOverlayBanner(const char *message, uint32_t durationMs, uint8_t options, std::function<void(int)> bannerCallback,
int8_t InitialSelected)
void Screen::showSimpleBanner(const char *message, uint32_t durationMs)
{
BannerOverlayOptions options;
options.message = message;
options.durationMs = durationMs;
options.notificationType = notificationTypeEnum::text_banner;
showOverlayBanner(options);
}
// Called to trigger a banner with custom message and duration
void Screen::showOverlayBanner(BannerOverlayOptions banner_overlay_options)
{
#ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
#endif
// Store the message and set the expiration timestamp
strncpy(NotificationRenderer::alertBannerMessage, banner_overlay_options.message, 255);
NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination
NotificationRenderer::alertBannerUntil =
(banner_overlay_options.durationMs == 0) ? 0 : millis() + banner_overlay_options.durationMs;
NotificationRenderer::optionsArrayPtr = banner_overlay_options.optionsArrayPtr;
NotificationRenderer::optionsEnumPtr = banner_overlay_options.optionsEnumPtr;
NotificationRenderer::alertBannerOptions = banner_overlay_options.optionsCount;
NotificationRenderer::alertBannerCallback = banner_overlay_options.bannerCallback;
NotificationRenderer::curSelected = banner_overlay_options.InitialSelected;
NotificationRenderer::pauseBanner = false;
NotificationRenderer::current_notification_type = notificationTypeEnum::selection_picker;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
ui->setTargetFPS(60);
ui->update();
}
// Called to trigger a banner with custom message and duration
void Screen::showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback)
{
#ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
#endif
nodeDB->pause_sort(true);
// Store the message and set the expiration timestamp
strncpy(NotificationRenderer::alertBannerMessage, message, 255);
NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination
NotificationRenderer::alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs;
NotificationRenderer::alertBannerOptions = options;
NotificationRenderer::alertBannerCallback = bannerCallback;
NotificationRenderer::curSelected = InitialSelected;
NotificationRenderer::pauseBanner = false;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawAlertBannerOverlay};
NotificationRenderer::curSelected = 0;
NotificationRenderer::current_notification_type = notificationTypeEnum::node_picker;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
setFastFramerate(); // Draw ASAP
ui->setTargetFPS(60);
ui->update();
}
// Called to trigger a banner with custom message and duration
void Screen::showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits,
std::function<void(uint32_t)> bannerCallback)
{
#ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip full refresh for all overlay menus
#endif
// Store the message and set the expiration timestamp
strncpy(NotificationRenderer::alertBannerMessage, message, 255);
NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination
NotificationRenderer::alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs;
NotificationRenderer::alertBannerCallback = bannerCallback;
NotificationRenderer::pauseBanner = false;
NotificationRenderer::curSelected = 0;
NotificationRenderer::current_notification_type = notificationTypeEnum::number_picker;
NotificationRenderer::numDigits = digits;
NotificationRenderer::currentNumber = 0;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
ui->setTargetFPS(60);
ui->update();
}
@@ -204,7 +267,7 @@ float Screen::estimatedHeading(double lat, double lon)
if (d < 10) // haven't moved enough, just keep current bearing
return b;
b = GeoCoord::bearing(oldLat, oldLon, lat, lon);
b = GeoCoord::bearing(oldLat, oldLon, lat, lon) * RAD_TO_DEG;
oldLat = lat;
oldLon = lon;
@@ -226,6 +289,20 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32)
{
graphics::normalFrames = new FrameCallback[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
LOG_INFO("Protobuf Value uiconfig.screen_rgb_color: %d", uiconfig.screen_rgb_color);
int32_t rawRGB = uiconfig.screen_rgb_color;
if (rawRGB > 0 && rawRGB <= 255255255) {
uint8_t TFT_MESH_r = (rawRGB >> 16) & 0xFF;
uint8_t TFT_MESH_g = (rawRGB >> 8) & 0xFF;
uint8_t TFT_MESH_b = rawRGB & 0xFF;
LOG_INFO("Values of r,g,b: %d, %d, %d", TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
if (TFT_MESH_r <= 255 && TFT_MESH_g <= 255 && TFT_MESH_b <= 255) {
TFT_MESH = COLOR565(TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
}
}
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
dispdev = new SH1106Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
@@ -235,13 +312,13 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
ST7789_MISO, ST7789_SCK);
#else
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
static_cast<ST7789Spi *>(dispdev)->setRGB(COLOR565(255, 255, 128));
#endif
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
#elif defined(USE_SSD1306)
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS)
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
@@ -288,9 +365,6 @@ void Screen::doDeepSleep()
{
#ifdef USE_EINK
setOn(false, graphics::UIRenderer::drawDeepSleepFrame);
#ifdef PIN_EINK_EN
digitalWrite(PIN_EINK_EN, LOW); // power off backlight
#endif
#else
// Without E-Ink display:
setOn(false);
@@ -309,13 +383,19 @@ 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
#ifdef PIN_EINK_EN
if (uiconfig.screen_brightness == 1)
digitalWrite(PIN_EINK_EN, HIGH);
#elif defined(PCA_PIN_EINK_EN)
if (uiconfig.screen_brightness == 1)
io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
#endif
#if defined(ST7789_CS) && \
!defined(M5STACK) // set display brightness when turning on screens. Just moved function from TFTDisplay to here.
static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness);
@@ -323,10 +403,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);
@@ -348,11 +425,13 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
// eInkScreensaver parameter is usually NULL (default argument), default frame used instead
setScreensaverFrames(einkScreensaver);
#endif
#ifdef ELECROW_ThinkNode_M1
if (digitalRead(PIN_EINK_EN) == HIGH) {
digitalWrite(PIN_EINK_EN, LOW);
}
#ifdef PIN_EINK_EN
digitalWrite(PIN_EINK_EN, LOW);
#elif defined(PCA_PIN_EINK_EN)
io.digitalWrite(PCA_PIN_EINK_EN, LOW);
#endif
dispdev->displayOff();
#ifdef USE_ST7789
SPI1.end();
@@ -382,9 +461,22 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
void Screen::setup()
{
// === Enable display rendering ===
useDisplay = true;
// === Load saved brightness from UI config ===
// For OLED displays (SSD1306), default brightness is 255 if not set
if (uiconfig.screen_brightness == 0) {
#if defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
brightness = 255; // Default for OLED
#else
brightness = BRIGHTNESS_DEFAULT;
#endif
} else {
brightness = uiconfig.screen_brightness;
}
// === Detect OLED subtype (if supported by board variant) ===
#ifdef AutoOLEDWire_h
if (isAUTOOled)
@@ -412,10 +504,17 @@ void Screen::setup()
ui->disableAllIndicators(); // Disable page indicator dots
ui->getUiState()->userData = this; // Allow static callbacks to access Screen instance
// === Apply loaded brightness ===
#if defined(ST7789_CS)
static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness);
#elif defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
dispdev->setBrightness(brightness);
#endif
LOG_INFO("Applied screen brightness: %d", brightness);
// === Set custom overlay callbacks ===
static OverlayCallback overlays[] = {
graphics::UIRenderer::drawFunctionOverlay, // For mute/buzzer modifiers etc.
graphics::UIRenderer::drawNavigationBar // Custom indicator icons for each frame
graphics::UIRenderer::drawNavigationBar // Custom indicator icons for each frame
};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
@@ -451,7 +550,7 @@ void Screen::setup()
#else
if (!config.display.flip_screen) {
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS)
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
#elif defined(USE_ST7789)
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
@@ -472,6 +571,7 @@ void Screen::setup()
// === Turn on display and trigger first draw ===
handleSetOn(true);
determineResolution(dispdev->height(), dispdev->width());
ui->update();
#ifndef USE_EINK
ui->update(); // Some SSD1306 clones drop the first draw, so run twice
@@ -486,7 +586,7 @@ void Screen::setup()
touchScreenImpl1->init();
}
}
#elif HAS_TOUCHSCREEN
#elif HAS_TOUCHSCREEN && !defined(USE_EINK)
touchScreenImpl1 =
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
touchScreenImpl1->init();
@@ -542,6 +642,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
}
@@ -558,6 +663,7 @@ int32_t Screen::runOnce()
if (displayHeight == 0) {
displayHeight = dispdev->getHeight();
}
menuHandler::handleMenuSwitch(dispdev);
// Show boot screen for first logo_timeout seconds, then switch to normal operation.
// serialSinceMsec adjusts for additional serial wait time during nRF52 bootup
@@ -586,11 +692,11 @@ int32_t Screen::runOnce()
#ifndef DISABLE_WELCOME_UNSET
if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
LoraRegionPicker(0);
menuHandler::OnboardMessage();
}
#endif
if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) {
showOverlayBanner("Rebooting...", 0);
showSimpleBanner("Rebooting...", 0);
}
// Process incoming commands.
@@ -637,6 +743,8 @@ int32_t Screen::runOnce()
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame
setFrames();
break;
case Cmd::NOOP:
break;
default:
LOG_ERROR("Invalid screen cmd");
}
@@ -763,38 +871,14 @@ 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;
indicatorIcons.clear();
size_t numframes = 0;
moduleFrames = MeshModule::GetMeshModulesWithUIFrames();
LOG_DEBUG("Show %d module frames", moduleFrames.size());
// put all of the module frames first.
// this is a little bit of a dirty hack; since we're going to call
// the same drawModuleFrame handler here for all of these module frames
// and then we'll just assume that the state->currentFrame value
// is the same offset into the moduleFrames vector
// so that we can invoke the module's callback
for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) {
// Draw the module frame, using the hack described above
normalFrames[numframes] = drawModuleFrame;
// Check if the module being drawn has requested focus
// We will honor this request later, if setFrames was triggered by a UIFrameEvent
MeshModule *m = *i;
if (m->isRequestingFocus())
fsi.positions.focusedModule = numframes;
if (m == waypointModule)
fsi.positions.waypoint = numframes;
indicatorIcons.push_back(icon_module);
numframes++;
}
LOG_DEBUG("Added modules. numframes: %d", numframes);
// If we have a critical fault, show it first
fsi.positions.fault = numframes;
@@ -806,9 +890,9 @@ void Screen::setFrames(FrameFocus focus)
#if defined(DISPLAY_CLOCK_FRAME)
fsi.positions.clock = numframes;
normalFrames[numframes++] = graphics::ClockRenderer::digitalWatchFace ? graphics::ClockRenderer::drawDigitalClockFrame
: &graphics::ClockRenderer::drawAnalogClockFrame;
indicatorIcons.push_back(icon_clock);
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
: graphics::ClockRenderer::drawDigitalClockFrame;
indicatorIcons.push_back(digital_icon_clock);
#endif
// Declare this early so its available in FOCUS_PRESERVE block
@@ -823,22 +907,27 @@ void Screen::setFrames(FrameFocus focus)
indicatorIcons.push_back(icon_mail);
#ifndef USE_EINK
fsi.positions.nodelist = numframes;
normalFrames[numframes++] = graphics::NodeListRenderer::drawDynamicNodeListScreen;
indicatorIcons.push_back(icon_nodes);
#endif
// Show detailed node views only on E-Ink builds
#ifdef USE_EINK
fsi.positions.nodelist_lastheard = numframes;
normalFrames[numframes++] = graphics::NodeListRenderer::drawLastHeardScreen;
indicatorIcons.push_back(icon_nodes);
fsi.positions.nodelist_hopsignal = numframes;
normalFrames[numframes++] = graphics::NodeListRenderer::drawHopSignalScreen;
indicatorIcons.push_back(icon_signal);
fsi.positions.nodelist_distance = numframes;
normalFrames[numframes++] = graphics::NodeListRenderer::drawDistanceScreen;
indicatorIcons.push_back(icon_distance);
#endif
#if HAS_GPS
fsi.positions.nodelist_bearings = numframes;
normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses;
indicatorIcons.push_back(icon_list);
@@ -858,26 +947,11 @@ void Screen::setFrames(FrameFocus focus)
}
#if !defined(DISPLAY_CLOCK_FRAME)
fsi.positions.clock = numframes;
normalFrames[numframes++] = graphics::ClockRenderer::drawDigitalClockFrame;
indicatorIcons.push_back(icon_clock);
normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame
: graphics::ClockRenderer::drawDigitalClockFrame;
indicatorIcons.push_back(digital_icon_clock);
#endif
// We don't show the node info of our node (if we have it yet - we should)
size_t numMeshNodes = nodeDB->getNumMeshNodes();
if (numMeshNodes > 0)
numMeshNodes--;
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) {
if (fsi.positions.firstFavorite == 255)
fsi.positions.firstFavorite = numframes;
fsi.positions.lastFavorite = numframes;
normalFrames[numframes++] = graphics::UIRenderer::drawNodeInfo;
indicatorIcons.push_back(icon_node);
}
}
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
if (!dismissedFrames.wifi && isWifiAvailable()) {
fsi.positions.wifi = numframes;
@@ -886,6 +960,64 @@ void Screen::setFrames(FrameFocus focus)
}
#endif
// Beware of what changes you make in this code!
// We pass numframes into GetMeshModulesWithUIFrames() which is highly important!
// Inside of that callback, goes over to MeshModule.cpp and we run
// modulesWithUIFrames.resize(startIndex, nullptr), to insert nullptr
// entries until we're ready to start building the matching entries.
// We are doing our best to keep the normalFrames vector
// and the moduleFrames vector in lock step.
moduleFrames = MeshModule::GetMeshModulesWithUIFrames(numframes);
LOG_DEBUG("Show %d module frames", moduleFrames.size());
for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) {
// Draw the module frame, using the hack described above
if (*i != nullptr) {
normalFrames[numframes] = drawModuleFrame;
// Check if the module being drawn has requested focus
// We will honor this request later, if setFrames was triggered by a UIFrameEvent
MeshModule *m = *i;
if (m && m->isRequestingFocus())
fsi.positions.focusedModule = numframes;
if (m && m == waypointModule)
fsi.positions.waypoint = numframes;
indicatorIcons.push_back(icon_module);
numframes++;
}
}
LOG_DEBUG("Added modules. numframes: %d", numframes);
// We don't show the node info of our node (if we have it yet - we should)
size_t numMeshNodes = nodeDB->getNumMeshNodes();
if (numMeshNodes > 0)
numMeshNodes--;
// Temporary array to hold favorite node frames
std::vector<FrameCallback> favoriteFrames;
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) {
favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo);
}
}
// Insert favorite frames *after* collecting them all
if (!favoriteFrames.empty()) {
fsi.positions.firstFavorite = numframes;
for (const auto &f : favoriteFrames) {
normalFrames[numframes++] = f;
indicatorIcons.push_back(icon_node);
}
fsi.positions.lastFavorite = numframes - 1;
} else {
fsi.positions.firstFavorite = 255;
fsi.positions.lastFavorite = 255;
}
fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
this->frameCount = numframes; // ✅ Save frame count for use in custom overlay
LOG_DEBUG("Finished build frames. numframes: %d", numframes);
@@ -894,11 +1026,10 @@ void Screen::setFrames(FrameFocus focus)
ui->disableAllIndicators();
// Add overlays: frame icons and alert banner)
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawAlertBannerOverlay};
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
// just changed)
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list just changed)
// Focus on a specific frame, in the frame set we just created
switch (focus) {
@@ -917,6 +1048,14 @@ void Screen::setFrames(FrameFocus focus)
// If no module requested focus, will show the first frame instead
ui->switchToFrame(fsi.positions.focusedModule);
break;
case FOCUS_CLOCK:
// Whichever frame was marked by MeshModule::requestFocus(), if any
// If no module requested focus, will show the first frame instead
ui->switchToFrame(fsi.positions.clock);
break;
case FOCUS_SYSTEM:
ui->switchToFrame(fsi.positions.memory);
break;
case FOCUS_PRESERVE:
// No more adjustment — force stay on same index
@@ -1127,8 +1266,12 @@ 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
// Only wake/force display if the configuration allows it
if (shouldWakeOnReceivedMessage()) {
setOn(true); // Wake up the screen first
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;
@@ -1160,7 +1303,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
}
}
screen->showOverlayBanner(banner, 3000);
screen->showSimpleBanner(banner, 3000);
}
}
@@ -1199,29 +1342,15 @@ int Screen::handleInputEvent(const InputEvent *event)
setFastFramerate(); // Draw ASAP
#endif
if (NotificationRenderer::isOverlayBannerShowing()) {
NotificationRenderer::inEvent = event->inputEvent;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar,
NotificationRenderer::drawAlertBannerOverlay};
NotificationRenderer::inEvent = *event;
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawBannercallback};
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
setFastFramerate(); // Draw ASAP
ui->update();
menuHandler::handleMenuSwitch(dispdev);
return 0;
}
/*
#if defined(DISPLAY_CLOCK_FRAME)
// For the T-Watch, intercept touches to the 'toggle digital/analog watch face' button
uint8_t watchFaceFrame = error_code ? 1 : 0;
if (this->ui->getUiState()->currentFrame == watchFaceFrame && event->touchX >= 204 && event->touchX <= 240 &&
event->touchY >= 204 && event->touchY <= 240) {
screen->digitalWatchFace = !screen->digitalWatchFace;
setFrames();
return 0;
}
#endif
*/
// Use left or right input from a keyboard to move between frames,
// so long as a mesh module isn't using these events for some other purpose
@@ -1230,7 +1359,7 @@ int Screen::handleInputEvent(const InputEvent *event)
// Ask any MeshModules if they're handling keyboard input right now
bool inputIntercepted = false;
for (MeshModule *module : moduleFrames) {
if (module->interceptingKeyboardInput())
if (module && module->interceptingKeyboardInput())
inputIntercepted = true;
}
@@ -1242,128 +1371,36 @@ int Screen::handleInputEvent(const InputEvent *event)
showNextFrame();
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
const char *banner_message;
int options;
if (kb_found) {
banner_message = "Action?\nBack\nSleep Screen\nNew Preset Msg\nNew Freetext Msg";
options = 4;
} else {
banner_message = "Action?\nBack\nSleep Screen\nNew Preset Msg";
options = 3;
}
showOverlayBanner(banner_message, 30000, options, [](int selected) -> void {
if (selected == 1) {
screen->setOn(false);
} else if (selected == 2) {
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
} else if (selected == 3) {
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST);
}
});
#if HAS_TFT
menuHandler::homeBaseMenu();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.memory) {
showOverlayBanner("Switch to MUI?\nYes\nNo", 30000, 2, [](int selected) -> void {
if (selected == 0) {
config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR;
config.bluetooth.enabled = false;
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
});
#else
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.memory) {
showOverlayBanner(
"Beeps Mode\nAll Enabled\nDisabled\nNotifications\nSystem Only", 30000, 4,
[](int selected) -> void {
config.device.buzzer_mode = (meshtastic_Config_DeviceConfig_BuzzerMode)selected;
service->reloadConfig(SEGMENT_CONFIG);
},
config.device.buzzer_mode);
#endif
menuHandler::systemBaseMenu();
#if HAS_GPS
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.gps && gps) {
showOverlayBanner(
"Toggle GPS\nBack\nEnabled\nDisabled", 30000, 3,
[](int selected) -> void {
if (selected == 1) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
playGPSEnableBeep();
gps->enable();
} else if (selected == 2) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED;
playGPSDisableBeep();
gps->disable();
}
service->reloadConfig(SEGMENT_CONFIG);
},
config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED ? 1
: 2); // set inital selection
menuHandler::positionBaseMenu();
#endif
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.clock) {
TZPicker();
menuHandler::clockMenu();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) {
LoraRegionPicker();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage &&
devicestate.rx_text_message.from) {
const char *banner_message;
int options;
if (kb_found) {
banner_message = "Message Action?\nBack\nDismiss\nReply via Preset\nReply via Freetext";
options = 4;
menuHandler::LoraRegionPicker();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) {
if (devicestate.rx_text_message.from) {
menuHandler::messageResponseMenu();
} else {
banner_message = "Message Action?\nBack\nDismiss\nReply via Preset";
options = 3;
menuHandler::textMessageBaseMenu();
}
#ifdef HAS_I2S
banner_message = "Message Action?\nBack\nDismiss\nReply via Preset\nReply via Freetext\nRead Aloud";
options = 5;
#endif
showOverlayBanner(banner_message, 30000, options, [](int selected) -> void {
if (selected == 1) {
screen->dismissCurrentFrame();
} else if (selected == 2) {
if (devicestate.rx_text_message.to == NODENUM_BROADCAST) {
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST,
devicestate.rx_text_message.channel);
} else {
cannedMessageModule->LaunchWithDestination(devicestate.rx_text_message.from);
}
} else if (selected == 3) {
if (devicestate.rx_text_message.to == NODENUM_BROADCAST) {
cannedMessageModule->LaunchFreetextWithDestination(NODENUM_BROADCAST,
devicestate.rx_text_message.channel);
} else {
cannedMessageModule->LaunchFreetextWithDestination(devicestate.rx_text_message.from);
}
}
#ifdef HAS_I2S
else if (selected == 4) {
const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
const char *msg = reinterpret_cast<const char *>(mp.decoded.payload.bytes);
audioThread->readAloud(msg);
}
#endif
});
} else if (framesetInfo.positions.firstFavorite != 255 &&
this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite &&
this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) {
const char *banner_message;
int options;
if (kb_found) {
banner_message = "Message Node?\nCancel\nNew Preset Msg\nNew Freetext Msg";
options = 3;
} else {
banner_message = "Message Node?\nCancel\nConfirm";
options = 2;
}
showOverlayBanner(banner_message, 30000, options, [](int selected) -> void {
if (selected == 1) {
cannedMessageModule->LaunchWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
} else if (selected == 2) {
cannedMessageModule->LaunchFreetextWithDestination(graphics::UIRenderer::currentFavoriteNodeNum);
}
});
menuHandler::favoriteBaseMenu();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist ||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_lastheard ||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal ||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_distance ||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal ||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_bearings) {
menuHandler::nodeListMenu();
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.wifi) {
menuHandler::wifiBaseMenu();
}
} else if (event->inputEvent == INPUT_BROKER_BACK) {
showPrevFrame();
@@ -1376,12 +1413,13 @@ int Screen::handleInputEvent(const InputEvent *event)
return 0;
}
int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
int Screen::handleAdminMessage(AdminModule_ObserverData *arg)
{
switch (arg->which_payload_variant) {
switch (arg->request->which_payload_variant) {
// Node removed manually (i.e. via app)
case meshtastic_AdminMessage_remove_by_nodenum_tag:
setFrames(FOCUS_PRESERVE);
*arg->result = AdminMessageHandleResult::HANDLED;
break;
// Default no-op, in case the admin message observable gets used by other classes in future
@@ -1396,97 +1434,28 @@ bool Screen::isOverlayBannerShowing()
return NotificationRenderer::isOverlayBannerShowing();
}
void Screen::LoraRegionPicker(uint32_t duration)
{
showOverlayBanner(
"Set the LoRa "
"region\nBack\nUS\nEU_433\nEU_868\nCN\nJP\nANZ\nKR\nTW\nRU\nIN\nNZ_865\nTH\nLORA_24\nUA_433\nUA_868\nMY_433\nMY_"
"919\nSG_"
"923\nPH_433\nPH_868\nPH_915\nANZ_433",
duration, 23,
[](int selected) -> void {
if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) {
config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected);
// This is needed as we wait til picking the LoRa region to generate keys for the first time.
if (!owner.is_licensed) {
bool keygenSuccess = false;
if (config.security.private_key.size == 32) {
// public key is derived from private, so this will always have the same result.
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
keygenSuccess = true;
}
} else {
LOG_INFO("Generate new PKI keys");
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
keygenSuccess = true;
}
if (keygenSuccess) {
config.security.public_key.size = 32;
config.security.private_key.size = 32;
owner.public_key.size = 32;
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
}
}
config.lora.tx_enabled = true;
initRegion();
if (myRegion->dutyCycle < 100) {
config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit
}
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
}
},
0);
}
void Screen::TZPicker()
{
showOverlayBanner(
"Pick "
"Timezone\nBack\nUS/Hawaii\nUS/Alaska\nUS/Pacific\nUS/Mountain\nUS/Central\nUS/Eastern\nUTC\nEU/Western\nEU/"
"Central\nEU/Eastern\nAsia/Kolkata\nAsia/Hong_Kong\nAU/AWST\nAU/ACST\nAU/AEST\nPacific/NZ",
30000, 17, [](int selected) -> void {
if (selected == 1) { // Hawaii
strncpy(config.device.tzdef, "HST10", sizeof(config.device.tzdef));
} else if (selected == 2) { // Alaska
strncpy(config.device.tzdef, "AKST9AKDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
} else if (selected == 3) { // Pacific
strncpy(config.device.tzdef, "PST8PDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
} else if (selected == 4) { // Mountain
strncpy(config.device.tzdef, "MST7MDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
} else if (selected == 5) { // Central
strncpy(config.device.tzdef, "CST6CDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
} else if (selected == 6) { // Eastern
strncpy(config.device.tzdef, "EST5EDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef));
} else if (selected == 7) { // UTC
strncpy(config.device.tzdef, "UTC", sizeof(config.device.tzdef));
} else if (selected == 8) { // EU/Western
strncpy(config.device.tzdef, "GMT0BST,M3.5.0/1,M10.5.0", sizeof(config.device.tzdef));
} else if (selected == 9) { // EU/Central
strncpy(config.device.tzdef, "CET-1CEST,M3.5.0,M10.5.0/3", sizeof(config.device.tzdef));
} else if (selected == 10) { // EU/Eastern
strncpy(config.device.tzdef, "EET-2EEST,M3.5.0/3,M10.5.0/4", sizeof(config.device.tzdef));
} else if (selected == 11) { // Asia/Kolkata
strncpy(config.device.tzdef, "IST-5:30", sizeof(config.device.tzdef));
} else if (selected == 12) { // China
strncpy(config.device.tzdef, "HKT-8", sizeof(config.device.tzdef));
} else if (selected == 13) { // AU/AWST
strncpy(config.device.tzdef, "AWST-8", sizeof(config.device.tzdef));
} else if (selected == 14) { // AU/ACST
strncpy(config.device.tzdef, "ACST-9:30ACDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef));
} else if (selected == 15) { // AU/AEST
strncpy(config.device.tzdef, "AEST-10AEDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef));
} else if (selected == 16) { // NZ
strncpy(config.device.tzdef, "NZST-12NZDT,M9.5.0,M4.1.0/3", sizeof(config.device.tzdef));
}
setenv("TZ", config.device.tzdef, 1);
service->reloadConfig(SEGMENT_CONFIG);
});
}
} // namespace graphics
#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

@@ -5,10 +5,28 @@
#include "detect/ScanI2C.h"
#include "mesh/generated/meshtastic/config.pb.h"
#include <OLEDDisplay.h>
#include <functional>
#include <string>
#include <vector>
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
namespace graphics
{
enum notificationTypeEnum { none, text_banner, selection_picker, node_picker, number_picker };
struct BannerOverlayOptions {
const char *message;
uint32_t durationMs = 30000;
const char **optionsArrayPtr = nullptr;
const int *optionsEnumPtr = nullptr;
uint8_t optionsCount = 0;
std::function<void(int)> bannerCallback = nullptr;
int8_t InitialSelected = 0;
notificationTypeEnum notificationType = notificationTypeEnum::text_banner;
};
} // namespace graphics
bool shouldWakeOnReceivedMessage();
#if !HAS_SCREEN
#include "power.h"
@@ -24,6 +42,8 @@ class Screen
FOCUS_FAULT,
FOCUS_TEXTMESSAGE,
FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
FOCUS_CLOCK,
FOCUS_SYSTEM,
};
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
@@ -38,10 +58,8 @@ class Screen
void setFunctionSymbol(std::string) {}
void removeFunctionSymbol(std::string) {}
void startAlert(const char *) {}
void showOverlayBanner(const char *message, uint32_t durationMs = 3000, uint8_t options = 0,
std::function<void(int)> bannerCallback = NULL, int8_t InitialSelected = 0)
{
}
void showSimpleBanner(const char *message, uint32_t durationMs = 0) {}
void showOverlayBanner(BannerOverlayOptions) {}
void setFrames(FrameFocus focus) {}
void endAlert() {}
};
@@ -76,8 +94,10 @@ class Screen
#include "commands.h"
#include "concurrency/LockGuard.h"
#include "concurrency/OSThread.h"
#include "graphics/draw/MenuHandler.h"
#include "input/InputBroker.h"
#include "mesh/MeshModule.h"
#include "modules/AdminModule.h"
#include "power.h"
#include <string>
#include <vector>
@@ -193,10 +213,11 @@ class Screen : public concurrency::OSThread
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent); // Sent by Mesh Modules
CallbackObserver<Screen, const InputEvent *> inputObserver =
CallbackObserver<Screen, const InputEvent *>(this, &Screen::handleInputEvent);
CallbackObserver<Screen, const meshtastic_AdminMessage *> adminMessageObserver =
CallbackObserver<Screen, const meshtastic_AdminMessage *>(this, &Screen::handleAdminMessage);
CallbackObserver<Screen, AdminModule_ObserverData *> adminMessageObserver =
CallbackObserver<Screen, AdminModule_ObserverData *>(this, &Screen::handleAdminMessage);
public:
OLEDDisplay *getDisplayDevice() { return dispdev; }
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
size_t frameCount = 0; // Total number of active frames
~Screen();
@@ -208,6 +229,8 @@ class Screen : public concurrency::OSThread
FOCUS_FAULT,
FOCUS_TEXTMESSAGE,
FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
FOCUS_CLOCK,
FOCUS_SYSTEM,
};
// Regenerate the normal set of frames, focusing a specific frame if requested
@@ -285,8 +308,17 @@ class Screen : public concurrency::OSThread
enqueueCmd(cmd);
}
void showOverlayBanner(const char *message, uint32_t durationMs = 3000, uint8_t options = 0,
std::function<void(int)> bannerCallback = NULL, int8_t InitialSelected = 0);
void showSimpleBanner(const char *message, uint32_t durationMs = 0);
void showOverlayBanner(BannerOverlayOptions);
void showNodePicker(const char *message, uint32_t durationMs, std::function<void(uint32_t)> bannerCallback);
void showNumberPicker(const char *message, uint32_t durationMs, uint8_t digits, std::function<void(uint32_t)> bannerCallback);
void requestMenu(graphics::menuHandler::screenMenus menuToShow)
{
graphics::menuHandler::menuQueue = menuToShow;
runNow();
}
void startFirmwareUpdateScreen()
{
@@ -300,7 +332,7 @@ class Screen : public concurrency::OSThread
void setHeading(long _heading)
{
hasCompass = true;
compassHeading = _heading;
compassHeading = fmod(_heading, 360);
}
bool hasHeading() { return hasCompass; }
@@ -320,6 +352,12 @@ class Screen : public concurrency::OSThread
/// Stops showing the boot screen.
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
void runNow()
{
setFastFramerate();
enqueueCmd(ScreenCmd{.cmd = Cmd::NOOP});
}
/// Overrides the default utf8 character conversion, to replace empty space with question marks
static char customFontTableLookup(const uint8_t ch)
{
@@ -544,7 +582,7 @@ class Screen : public concurrency::OSThread
int handleTextMessage(const meshtastic_MeshPacket *arg);
int handleUIFrameEvent(const UIFrameEvent *arg);
int handleInputEvent(const InputEvent *arg);
int handleAdminMessage(const meshtastic_AdminMessage *arg);
int handleAdminMessage(AdminModule_ObserverData *arg);
/// Used to force (super slow) eink displays to draw critical frames
void forceDisplay(bool forceUiUpdate = false);
@@ -601,8 +639,6 @@ class Screen : public concurrency::OSThread
void handleShowNextFrame();
void handleShowPrevFrame();
void handleStartFirmwareUpdateScreen();
void TZPicker();
void LoraRegionPicker(uint32_t duration = 30000);
// Info collected by setFrames method.
// Index location of specific frames.
@@ -611,7 +647,6 @@ class Screen : public concurrency::OSThread
struct FramesetInfo {
struct FramePositions {
uint8_t fault = 255;
uint8_t textMessage = 255;
uint8_t waypoint = 255;
uint8_t focusedModule = 255;
uint8_t log = 255;
@@ -621,6 +656,12 @@ class Screen : public concurrency::OSThread
uint8_t memory = 255;
uint8_t gps = 255;
uint8_t home = 255;
uint8_t textMessage = 255;
uint8_t nodelist = 255;
uint8_t nodelist_lastheard = 255;
uint8_t nodelist_hopsignal = 255;
uint8_t nodelist_distance = 255;
uint8_t nodelist_bearings = 255;
uint8_t clock = 255;
uint8_t firstFavorite = 255;
uint8_t lastFavorite = 255;
@@ -678,5 +719,6 @@ class Screen : public concurrency::OSThread
// Extern declarations for function symbols used in UIRenderer
extern std::vector<std::string> functionSymbol;
extern std::string functionSymbolString;
extern graphics::Screen *screen;
#endif

View File

@@ -16,7 +16,7 @@
#include "graphics/fonts/OLEDDisplayFontsCS.h"
#endif
#ifdef CROWPANEL_ESP32S3_5_EPAPER
#if defined(CROWPANEL_ESP32S3_5_EPAPER) && defined(USE_EINK)
#include "graphics/fonts/EinkDisplayFonts.h"
#endif
@@ -40,6 +40,9 @@
#ifdef OLED_PL
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_PL // Height: 19
#else
#ifdef OLED_RU
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_RU // Height: 19
#else
#ifdef OLED_UA
#define FONT_MEDIUM_LOCAL ArialMT_Plain_16_UA // Height: 19
#else
@@ -50,9 +53,13 @@
#endif
#endif
#endif
#endif
#ifdef OLED_PL
#define FONT_LARGE_LOCAL ArialMT_Plain_24_PL // Height: 28
#else
#ifdef OLED_RU
#define FONT_LARGE_LOCAL ArialMT_Plain_24_RU // Height: 28
#else
#ifdef OLED_UA
#define FONT_LARGE_LOCAL ArialMT_Plain_24_UA // Height: 28
#else
@@ -63,9 +70,10 @@
#endif
#endif
#endif
#endif
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS)) && \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
// The screen is bigger so use bigger fonts
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19
@@ -77,7 +85,7 @@
#define FONT_LARGE FONT_LARGE_LOCAL // Height: 28
#endif
#if defined(CROWPANEL_ESP32S3_5_EPAPER)
#if defined(CROWPANEL_ESP32S3_5_EPAPER) && defined(USE_EINK)
#undef FONT_SMALL
#undef FONT_MEDIUM
#undef FONT_LARGE

Some files were not shown because too many files have changed in this diff Show More