Compare commits

...

392 Commits

Author SHA1 Message Date
renovate[bot]
3767ce4a38 Update NimBLE-Arduino to v2 2025-12-13 21:47:01 +00:00
Jonathan Bennett
b74238194b Add JSON packet recording option to native (#8930) 2025-12-12 18:30:43 -06:00
Ben Meadors
5d5819b876 Skipp assertion on this test for now 2025-12-12 16:26:01 -06:00
Tom Fifield
f127702bef Fix GPS Buffer full issue on NRF52480 (Seeed T1000E) (#8956)
We set the buffer size to about a byte on NRF52480, less than
other platforms:

esp32.ini:  -DSERIAL_BUFFER_SIZE=4096
esp32c6.ini:  -DSERIAL_BUFFER_SIZE=4096
nrf52.ini:  -DSERIAL_BUFFER_SIZE=1024

However, 115200 baud, like the T1000e uses is about 12 times that
- almost 15 bytes per millisecond.
15 bytes * 200 millisecond (our GPS poll rate)  = 3000 bytes, which is longer than our buffer
on the nrf52 platform. This causes "GPS Buffer full" errors on the T1000e
and other devices based on NRF52480 with newer GPS chips.

This patch increases SERIAL_BUFFER_SIZE for nrf52480 to 4096 to align with
other platforms. It keeps the original 1024 for the nrf52832, which has
fewer resources.

Fixes https://github.com/meshtastic/firmware/issues/5767
2025-12-12 16:23:23 -06:00
Ben Meadors
cce8cbfe34 Mark implicit ACK for MQTT as MQTT transport (#8939) (#8947)
* Mark implicit ACK for MQTT as MQTT transport

* TRUNK

* Fix build

* Make sure implicit ACKs from MQTT do not stop retransmissions in ReliableRouter

---------

Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com>
2025-12-12 05:21:08 -06:00
github-actions[bot]
a4a6c3509a Upgrade trunk (#8946)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-12 05:20:12 -06:00
GUVWAF
68250dc937 Mark implicit ACK for MQTT as MQTT transport (#8939)
* Mark implicit ACK for MQTT as MQTT transport

* TRUNK

* Fix build

* Make sure implicit ACKs from MQTT do not stop retransmissions in ReliableRouter

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-12-12 05:19:32 -06:00
Igor Danilov
c8628b3422 Fix #8899 [Bug]: [TloraPager] RotaryEncoder crash (#8933)
* Fix #8899 [Bug]: [TloraPager] RotaryEncoder crash

* Apply Copilot review

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-12-11 19:04:15 -06:00
renovate[bot]
2ac74d6677 Update actions/cache action to v5 (#8944)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 19:03:14 -06:00
Ben Meadors
9d487ddc0d Merge pull request #8945 from meshtastic/develop
Develop to master
2025-12-11 19:02:56 -06:00
Austin
bcfe069997 Optimize builds to reduce duplicate dependency checks (#8943)
'mtjson' will now build all required pieces when they don't exist
2025-12-11 19:01:31 -06:00
Austin
4fc96bdf83 Use 'gh-action-runner' action for "Check" jobs. (#8938)
Everything's pre-baked, 503 no more!
2025-12-11 12:26:21 -06:00
renovate[bot]
4ef943f204 Update meshtastic/device-ui digest to 2746a1c (#8936)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-11 10:32:28 -06:00
Jonathan Bennett
a8fa5f25cb Properly turn off power pins at shutdown for m3 (#8935) 2025-12-11 10:23:45 -06:00
Ben Meadors
3b2a1547de More board_level extras 2025-12-11 06:23:08 -06:00
github-actions[bot]
6f725a1996 Upgrade trunk (#8932)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-11 05:25:46 -06:00
Ben Meadors
467c042bf7 Merge pull request #8929 from meshtastic/master
Master to dev
2025-12-10 20:48:03 -06:00
Ben Meadors
cc4c41167c Merge pull request #8928 from meshtastic/develop 2025-12-10 19:08:53 -06:00
Benjamin Faershtein
fff2bbf4a0 Use truncated position for smart position (#8906) 2025-12-10 19:05:26 -06:00
Jonathan Bennett
fba92229a6 Add I2C device check for seesaw device on native (#8927)
It turns out the logic here was attempting to access i2c without being told to do so. Not good, especially on desktops.
2025-12-10 18:01:52 -06:00
Jason P
ff0a4ea320 Update System Frame for improved rendering on devices (#8923) 2025-12-10 16:30:26 -06:00
Jonathan Bennett
83b603827c Enable Muzi-base LED notification (#8925) 2025-12-10 16:29:50 -06:00
Jason P
2032ff1c32 Create new screen colors for BaseUI (#8921)
* Create new colors for BaseUI

* Update Ice color
2025-12-10 11:09:37 -06:00
Alex Samorukov
5910cc2e26 Use PSRAM to reduce heap usage percentage on ESP32 with PSRAM (#8891)
* Use PSRAM for malloc > 256bytes to get more heap memory

* Use dynamic allocator on boards with PSRAM to free more heap

* Apply suggestion from @Copilot

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

* Move heap_caps_malloc_extmem_enable() to the top of the init

* Update src/main.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-12-10 06:23:23 -06:00
github-actions[bot]
ee80ec7b68 Upgrade trunk (#8922)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-10 06:14:00 -06:00
Austin
aa72e397f2 PIO: Fix closedcube lib reference (#8920)
Fixes ClosedCube reinstalling on every build
2025-12-09 16:40:37 -06:00
renovate[bot]
ec0dfb7337 Update peter-evans/create-pull-request action to v8 (#8919)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-09 15:56:27 -06:00
Austin
c55bea8460 ARCtastic (#8904) -- Do It Live!
Actions Runner Controller

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-12-09 15:11:07 -06:00
Austin
aa605fc4a2 Actions: Fix release manifest formating (#8918) 2025-12-09 14:27:13 -06:00
Igor Danilov
d75680a2dd Fix #8915 [Bug]: Exception Decoder does not recognize the backtrace (#8917) 2025-12-09 12:24:41 -06:00
Austin
817f3b9ec8 Update platformio/espressif32 to v6.12.0 (#7697) 2025-12-09 09:57:02 -06:00
Ben Meadors
decd58cd5c Merge pull request #8913 from meshtastic/revert-8858-nrf52-power-saving-1
Revert "Cut NRF52 bluetooth power usage by 300% - testers needed!"
2025-12-09 08:02:29 -06:00
Ben Meadors
e691bd9732 Revert "Cut NRF52 bluetooth power usage by 300% - testers needed! (#8858)"
This reverts commit ae8d3fbb3d.
2025-12-09 08:02:04 -06:00
Ben Meadors
6bad81f8dd Merge pull request #8911 from vidplace7/fix-chmod
Fix apply device-install permissions
2025-12-09 06:50:19 -06:00
Austin Lane
69b9977fc1 Fix apply device-install permissions
device-install.sh doesn't exist for non-esp32 targets
2025-12-09 07:48:30 -05:00
Ben Meadors
0726bb4b56 Merge pull request #8910 from meshtastic/develop
Develop to master
2025-12-09 06:04:59 -06:00
github-actions[bot]
6b11991be0 Upgrade trunk (#8856)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-09 06:03:52 -06:00
Ben Meadors
8e63dcf59a Merge branch 'master' into develop 2025-12-09 05:59:15 -06:00
Lewis He
042543eb25 Fixed the issue where T-Echo did not completely shut down peripherals upon power-off. (#8524)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-12-09 05:39:27 -06:00
phaseloop
ae8d3fbb3d Cut NRF52 bluetooth power usage by 300% - testers needed! (#8858)
* Improve NRF52 bluetooth power efficiency

* test T114 bad LFXO

* T1000 test

* force BLE param negotiation

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-12-08 19:59:14 -06:00
Austin
928739e0fb Renovate: fix malformed comment for wollewald/BH1750_WE (#8767) 2025-12-08 19:31:28 -06:00
Ben Meadors
8be7915fc7 Fix wm111111110 2025-12-08 19:19:10 -06:00
Ben Meadors
c052963395 Guard 2M PHY mode for NimBLE (#8890)
* Guard 2M PHY mode for NimBLE

* Update src/nimble/NimbleBluetooth.cpp

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

* Apply suggestion from @Copilot

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

* Apply suggestion from @Copilot

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

* Another #endif snuck in there

* Move endif

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-08 18:48:28 -06:00
Jonathan Bennett
65c418d4e1 Update protobuf name of FRIED_CHICKEN (#8903) 2025-12-09 11:13:59 +11:00
Jonathan Bennett
c3a69a2742 Fix backwards buttons on Thinknode-M1 (#8901) 2025-12-08 17:58:23 -06:00
Austin
66ff1536f3 Meshtastic build manifest (#8248) 2025-12-08 17:21:23 -06:00
simon-muzi
5671e9d96f Improved R1 Neo & muzi-base buzzer beeps for GPS on/off (#8870)
Matched the resonant frequency of the hardware buzzer to maximize volume for the turn on beep.

Further distinguished ON beep from OFF beep, making it easier for users to understand the state change.
2025-12-08 13:50:05 -06:00
Manuel
bd4bcb94f0 tryfix eink parameters (#8898) 2025-12-08 13:14:24 -06:00
Igor Danilov
4b2f241478 Disable vibration if needed (#8895) 2025-12-08 06:03:20 -06:00
Wilson
eb087849c0 OnScreenKeyboard Improvement with Joystick and UpDown Encoder (#8379)
* Add mesh/Default.h include.

* Reflacter OnScreenKeyBoard Module, do not interrupt keyboard when new message comes.

* feat: Add long press scrolling for Joystick and upDown Encoder on baseUI frames and menus.

* refactor: Clean up code formatting and improve readability in Screen and OnScreenKeyboardModule

* Fix navigation on UpDownEncoder, default was RotaryEncoder while bringing the T_LORA_PAGER

* Update src/graphics/draw/MenuHandler.cpp

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

* Update src/modules/OnScreenKeyboardModule.h

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

* Update src/graphics/draw/NotificationRenderer.cpp

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

* Optimize the detection logic for repeated events of the arrow keys.

* Fixed parameter names in the OnScreenKeyboardModule::start

* Trunk fix

* Reflator OnScreenKeyboard Input checking, make it simple

* Simplify long press logic in OnScreenKeyboardModule.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-08 05:40:30 -06:00
Igor Danilov
94aedff6ae Resolve #8887 (T-LoRaPager Vibration on New Message Delivery) (#8888)
* Resolve #8887

* Update src/modules/ExternalNotificationModule.cpp

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

* Update src/modules/ExternalNotificationModule.cpp

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

* use canBuzz method

* trunk fmt

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-12-07 20:29:18 -06:00
Igor Danilov
2ae391197f Fix #8883 (lora-Pager fter playing the notification, voltage does not disappear from the speaker) (#8884)
* Fix #8883

* Fix crash when delete not inicialized rtttlFile
2025-12-07 15:50:53 -06:00
Clive Blackledge
2a17c3b5d4 Change ARDUINO_USB_MODE from 0 to 1 in the board definition. This switches to the ESP32-S3's Hardware CDC and JTAG mode, which properly handles the reset signals for automatic reboot after firmware updates. (#8881) 2025-12-06 18:56:56 -06:00
Tom
8060134224 promicro doesn't need these. (#8873) 2025-12-06 13:25:57 +11:00
github-actions[bot]
eeaafda62a Update protobufs (#8871)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-12-05 10:41:48 -06:00
renovate[bot]
6e9fd189b4 Update meshtastic/device-ui digest to 4fb5f24 (#8862)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-04 15:33:19 -06:00
Donato
2f4eb25b2f Optimization flags for all NRF52 targets to reduce code size (#8854)
* changes of variants/nrf52840/nrf52.ini and variants/nrf52840/rak4631/platformio.ini

* try for nrf52 size reduction, faketec exclude unused radios and meshlink refactor

* can't exclude LR11X0 as in schematic there's option for LR1121

* remove -Map flag and -Wl

* removed spaces causing error

---------

Co-authored-by: macvenez <macvenez@gmail.com>
2025-12-04 15:32:42 -06:00
Tom
aa85fbbcc4 Promicro documentation update (#8864)
* Delete variants/nrf52840/diy/nrf52_promicro_diy_tcxo/Schematic_Pro-Micro_Pinouts.pdf

remove old file

* Add updated schematic

* Update GPS TX and RX pin definitions after swap

* Update GPS pin definitions and schematic link

Updated the schematic link to reflect GPS pin definition changes.
2025-12-04 13:35:50 -06:00
renovate[bot]
3f40916223 Update alpine Docker tag to v3.23 (#8853)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 15:30:09 -06:00
github-actions[bot]
1b4925bd07 Upgrade trunk (#8849)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-03 07:50:50 -06:00
renovate[bot]
0828c445fb Update actions/stale action to v10.1.1 (#8848)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 05:39:31 -06:00
Benjamin Faershtein
61e41a8beb Don't scale up the frequency of telemetry sending (#8664) 2025-12-02 13:59:05 -06:00
github-actions[bot]
90584359e4 Upgrade trunk (#8836)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-02 05:48:36 -06:00
Jonathan Bennett
8a43741589 Add 'cleanup' to required PR labels (#8835) 2025-12-02 05:48:19 -06:00
Jonathan Bennett
525c048354 Move device specific OCV curves to their respective device.h (#8834) 2025-12-02 05:46:24 -06:00
Jonathan Bennett
41cbd77db3 Move everything from /arch to /variant (#8831) 2025-12-02 08:56:55 +01:00
github-actions[bot]
f3e38a425f Automated version bumps (#8786)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-12-01 19:31:58 -06:00
rbomze
a11152e545 Commented out the definition of BATTERY_LPCOMP_INPUT in the Helltec T114 variant, due to power leakage of 2.9mA in off state. See bug #8801 (#8800)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-12-01 19:21:49 -06:00
Ben Meadors
859ae4d3d2 Plain RAK4631 should not compile EInk and TFT display code (#8811)
* Plain RAK4631 should not compile EInk and TFT display code

* Add USE_TFTDISPLAY to variant files.

* Derp

* Undo the platformio.ini changes to heltec_v4

* Drop unneeded src_filter lines

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Jason P <applewiz@mac.com>
2025-12-01 19:19:50 -06:00
Ben Meadors
03600b1252 Merge branch 'master' into develop 2025-12-01 15:40:16 -06:00
Ben Meadors
a3d3e1c912 Flags and scripts for size reduction on NRF52 -> Currently targeting … (#8825)
* Flags and scripts for size reduction on NRF52 -> Currently targeting rak4631

* Changes from the other branch poluted it

* Remove the stripper

* No strip
2025-12-01 15:34:05 -06:00
Austin
0e653056e7 RPM: Fix broken builds (bad backmerge) (#8787)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-12-01 08:00:10 -06:00
github-actions[bot]
eba6e4ed75 Upgrade trunk (#8822)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-12-01 06:16:52 -06:00
renovate[bot]
80e8745714 Update XPowersLib to v0.3.2 (#8823)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-01 06:14:46 -06:00
Chloe Bethel
ee6c9101c7 Make GPS_TX_PIN the serial TX and GPS_RX_PIN the serial RX for all NRF variants (#8772) 2025-11-30 21:57:25 -06:00
HarukiToreda
34f8300288 Initial Chatter 2.0 fix for baseUI (#8615)
* Initial Chatter 2.0 fix for baseUI

* trunk fix

---------

Co-authored-by: Jason P <applewiz@mac.com>
2025-11-30 21:32:51 -06:00
Riker
09bbfce625 Enabled MQTT and WEBSERVER by default (#8679)
Signed-off-by: kur1k0 <zhuzirun@m5stack.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-11-30 20:27:45 -06:00
Jonathan Bennett
5b1b420cad Add initial support for Hackaday Communicator (#8771)
* Add initial support for Hackaday Communicator

* Fork it!

* Trunk

* Remove unused elements from the HackadayCommunicatorKeyboard

* Don't divide by zero.
2025-11-30 17:21:10 -06:00
Jonathan Bennett
8899487c2f Modify power saving condition for WiFi (#8815)
Update preprocessor directive to require both HAS_WIFI and MESHTASTIC_EXCLUDE_WIFI conditions.
2025-11-30 17:18:03 -06:00
Jason P
430d55e5e8 Add WiFi Toggle to System frame to re-enable (#8802)
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-11-30 17:17:00 -06:00
Jonathan Bennett
5ef3ff7116 rework screen.cpp ifdefs (#8816) 2025-11-30 15:33:29 -06:00
renovate[bot]
1abf8ddb30 Update meshtastic/device-ui digest to 3bf3322 (#8814)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 13:28:58 -06:00
HarukiToreda
5a595a3ae7 Replace assert in UTF8 decoder to prevent unexpected reboot (#8807) 2025-11-30 07:45:24 -06:00
renovate[bot]
bcd4a1176a Update dorny/test-reporter action to v2.3.0 (#8809)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-30 06:10:08 -06:00
Jason P
0081cec207 Fix ifdef statement after ST7796 merge to resolve screen color issues (#8796) 2025-11-28 20:24:39 -06:00
Jonathan Bennett
94db3506bd Add LOG_POWERFSM and LOG_INPUT debug macros (#8791) 2025-11-28 19:58:52 -06:00
Jonathan Bennett
2f0fe4e5da Use the dedicated isVbusIn() function for detecting USB plug 2025-11-28 16:42:14 -06:00
github-actions[bot]
a59723030a Upgrade trunk (#8781)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-11-28 05:25:47 -06:00
Jonathan Bennett
de26dfe468 Remove screen activation in powerExit function (#8779)
This seems to be a potential source of unintended screen wakes.
2025-11-28 05:23:07 -06:00
Jason P
a6cdf2c50b - Correct vertical alignment for Muzi_Base on On Screen Keyboard (#8774) 2025-11-27 07:03:25 -06:00
Ben Meadors
1c43d71067 Merge branch 'master' into develop 2025-11-27 06:39:26 -06:00
Nasimovy
1523368c53 adding support for the ST7796 + creating a new variant of the T-beam (#6575)
* remove duplicate HAS_LP5562  introduced by #6422

* add ST7796

* changes to get display centered+lib update

* seperated from tbeam

* forgot the simple scan case

* lowered speeds to 1/4

* added SPI Speed to constructor+ cleaned up variant.h

* even slower speeds....

* add ST7796

* changes to get display centered+lib update

* seperated from tbeam

* forgot the simple scan case

* lowered speeds to 1/4

* added SPI Speed to constructor+ cleaned up variant.h

* even slower speeds....

* changed variant name to tbeam-displayshield

* modified variant.h and merged ini file+testing on lower spi frequency for the lora module, display shield pumps out EMI?

* try higher speeds + HSPI

* cleanup of redundant code

* refelct changes?

* trunk fmt

* testing touchscreen code

* further testing

* changed to sensorlib 0.3.1

* i broke it , dont know how to fix at the moment will investigate

* add -1 functionality for touch IRQ

* revert to working example?

* it works.... is pressed was not working properly

* working touchscreen but gestures not moving display

* swap XY+ mirror X

* cleanup + addition of  defines for on screen keyboard and canned message module

* removed debug lines, disabled bluetooth for now because of stack smashing protect failure

* reverted the revert #6640 + increased speed, bleutooth is stable now on reconnection cold booth etc , GPS is still not working though

* remove debug + add fixed baudrate for gps

* fmt

* revert NIMble

* changed display library to meshtastic org

* removed baudrate of 115200 and some commented out code

* Correct spelling

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

* Typo

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

* display speed x10

* resolve conflicts

* undo

* revert speed increase CPU

* add SCREEN_TRANSITION_FRAMERATE 5

* spi speed increase of the display

* using the original touchscreen implementation

* removal of H file line

* add USE_ST7796 to missing places

* removed is pressed + interrupt

* revert changes of settings.json

* update to screen.cpp

* test identification of CST226 and CST328

* Update src/configuration.h

typo

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

* made changes to detection because it was completely wrong, CST226SE has 2 posible adresses

* add merge queue

* try vars

* kerning in yaml.

* update comment

* lint etc

* touching to check grandfathering

* explicit ignores

* add WIP for Unit C6L (#7433)

* add WIP for Unit C6L
* adapt to new config structure
* Add c6l BLE and screen support (#7991)
* Minor c6l fix
* Move out of PRIVATE_HW
---------
Co-authored-by: Austin <vidplace7@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Jason P <Xaositek@users.noreply.github.com>
Co-authored-by: Markus <Links2004@users.noreply.github.com>

* Update Adafruit BusIO to v1.17.3 (#8018)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update actions/checkout action to v5 (#8020)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update actions/setup-python action to v6 (#8023)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Upgrade trunk (#8025)

Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>

* Update actions/download-artifact action to v5 (#8021)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Fix init for InputEvent (#8015)

* Automated version bumps (#8028)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Allow Left / Right Events for selection and improve encoder responsives (#8016)

* Allow Left / Right Events for selection and improve encoder responsives

* add define for ROTARY_DELAY

* T-Lora Pager: Support LR1121 and SX1280 models (#7956)

* T-Lora Pager: Support LR1121 and SX1280 models

* Remove ifdefs

* (resubmission) Manual GitHub actions to allow building one target or arch (#7997)

* Reset the modified files

* Fix some changes

* Fix some changes

* Trunk. That is all.

---------

Co-authored-by: Tom <116762865+Nestpebble@users.noreply.github.com>

* BaseUI Show/Hide Frame Functionality (#7382)

* Rename System Frame (from Memory) in code base

* Create menu options to Show/Hide frames: Node Lists, Bearings, Position, LoRa, Clock and Favorites frames

* Move Region Picker into submenu

* Tweak wording for Send Position vs Node Info if the device has GPS

* Update actions/checkout action to v5 (#8031)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update actions/download-artifact action to v5 (#8032)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update actions/setup-python action to v6 (#8033)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Phone GPS display on Position Screen for BaseUI (#7875)

* Phone GPS display on Position Screen

This is a PR to show when a phone shares GPS location with the node so you can reliably know what coordinate is being shared with the Mesh.

* Merge pull request #8004 from compumike/compumike/debug-heap-add-free-heap-debugging-to-all-log-lines

When `DEBUG_HEAP` is defined, add free heap bytes to every log line in `RedirectablePrint::log_to_serial`

* Feature: Seamless Cross-Preset Communication via UDP Multicast Bridging (#7753)

* Added compatibility between nodes on different Presets through `Mesh via UDP`

* Optimize multicast handling and channel mapping

- FloodingRouter: remove redundant UDP-encrypted rebroadcast suppression.
- Router: guard multicast fallback with HAS_UDP_MULTICAST and map fallback-decoded packets
  to the local default channel via isDefaultChannel()
- UdpMulticastHandler: set transport_mechanism only after successful decode

* trunk fmt

* Move setting transport mechanism.

---------

Co-authored-by: GUVWAF <thijs@havinga.eu>

* Auto-favorite remote admin node

* Merge pull request #7873 from compumike/compumike/client-base-role

Add `CLIENT_BASE` role: `ROUTER` for favorites, `CLIENT` otherwise (for attic/roof nodes!)

* Fixes

* BaseUI Updates (#7787)

* Account for low resolution wide screen OLEDs

* Allow picking of Device Role and new Display Formatter for Device Role

* Add remainder of client roles to display formatter

* Don't update the role unless you pick a value

* Mascots are fun

* Fix warnings during compile time

* Improve some menus

* Mascots need to work everywhere

* Update Chirpy image

* Fix Trunk

* Update protobufs

* Add date to Clock screen

* Analog clocks love dates too

* Finalize date moves for analog clock

* Added Last Coordinate counter to Position screen (#7865)

Adding a counter to show the last time a GPS coordinate was detected to ensure the user is aware how long since the coordinate updated or to identify any errors.

* Fix

* Portduino config refactor (#7796)

* Start portduino_config refactor

* refactor GPIOs to new portduino_config

* More portduino_config work

* More conversion to portduino_config

* Finish portduino_config transition

* trunk

* yaml output work

* Simplify the GPIO config

* Trunk

* updated shebang to use a more standard path for bash (#7922)

Signed-off-by: Trenton VanderWert <trenton.vanderwert@gmail.com>

* Show GPS Date properly in drawCommonHeader (#7887)

* Commit good code that is sustainable

* Fix new build errors

* BaseUI Updates (#7787)

* Account for low resolution wide screen OLEDs

* Allow picking of Device Role and new Display Formatter for Device Role

* Add remainder of client roles to display formatter

* Don't update the role unless you pick a value

* Mascots are fun

* Fix warnings during compile time

* Improve some menus

* Mascots need to work everywhere

* Update Chirpy image

* Fix Trunk

* Update protobufs

* Add date to Clock screen

* Analog clocks love dates too

* Finalize date moves for analog clock

* Add formatting and menu picking for other GPS format options (#7974)

* Add back options for other GPS format options

* Rename variables and don't overlap elements

* Fix default value

* Should probably add a menu while I'm here!

* Shorten names just a bit to fit on screens

* Fix off by one

* Labels try to make things better

* Missed a label

* Update protobufs (#8038)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Add formatting and menu picking for other GPS format options (#7974)

* Add back options for other GPS format options

* Rename variables and don't overlap elements

* Fix default value

* Should probably add a menu while I'm here!

* Shorten names just a bit to fit on screens

* Fix off by one

* Labels try to make things better

* Missed a label

* Add a new GPS model CM121. (#7852)

* Add a new GPS model CM121.

* Add CM121 to Unicore.

* Trunk fixes, remove unneded NMEA lines

---------

Co-authored-by: Tom Fifield <tom@tomfifield.net>

* (resubmission) Manual GitHub actions to allow building one target or arch (#7997)

* Reset the modified files

* Fix some changes

* Fix some changes

* Trunk. That is all.

---------

Co-authored-by: Tom <116762865+Nestpebble@users.noreply.github.com>

* PPA: Enable Ubuntu 25.10 (questing) (#7940)

* Update Protobuf usage, add MLS, fix clock (#8041)

* Update protobufs (#8045)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Fix icon

* C6l fixes (#8047)

* fix build with HAS_TELEMETRY 0 (#8051)

* Make sure to ACK ACKs/replies if next-hop routing is used (#8052)

* Make sure to ACK ACKs/replies if next-hop routing is used
To stop their retransmissions; hop limit of 0 is enough

* Update src/mesh/ReliableRouter.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>

* move HTTP contentTypes to Flash - saves 768 Bytes of RAM (#8055)

* Use `lora.use_preset` config to get name (#8057)

* Update RadioLib to v7.3.0 (#8065)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Fix Rotary Encoder Button (#8001)

this fixes the Rotary Encoder Button, currenlty its not working at all.
Currently the action `ROTARY_ACTION_PRESSED` is only triggerd with a IRQ on RISING, which results in nothing since the function detects the "not longer" pressed button --> no action.

the `ROTARY_ACTION_PRESSED` implementation needs to be called on both edges (on press and release of the button)

changing the interupt setting to `CHANGE` fixes the problem.

* Add another seeed_xiao_nrf52840_kit build environment for I2C pinout (#8036)

* Update platformio.ini

* Remove some more extraneous lines

* Add heltec_v4 board. (#7845)

* add heltec_v4 board.

* Update variants/esp32s3/heltec_v4/platformio.ini

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

* Limit the maximum output power.

* Trunk fixes

Fixes formatting to match meshtastic trunk linter.

* Apply suggestion from @Copilot

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

* 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: Austin <vidplace7@gmail.com>
Co-authored-by: Tom Fifield <tom@tomfifield.net>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Upgrade trunk (#8078)

Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>

* portduino bump to fix gpiod bug (#8083)

An earlier portduino causes problems with initializing gpiod lines. This pulls in the fix.

* Handle ext. notification module things even if not enabled (#8089)

* tlora-pager wake on button, and kb backlight toggling (#8090)

* Try-fix: Unstick that PhoneAPI state (#8091)

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>

* Also pull a deviceID from esp32c6 devices (#8092)

* Remove line from BLE pin screen, to make pin readible on tiny screens

* Fix build errors (#8067)

* Heltec V4 is 16mb

* Clear lasttoradio on BLE disconnect (#8095)

* On disconnect, clear the lastToRadio buffer

* Move it, bucko!

* Revert "Fix build errors (#8067)"

This reverts commit d998f70b56.

* Automated version bumps (#8100)

Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>

* Upgrade trunk (#8094)

Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>

* Update Adafruit BusIO to v1.17.4 (#8098)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Add three expansion screens for heltec mesh solar. (#7995)

* Add three expansion screens for heltec mesh solar.

* delete whitespace

Update variants/nrf52840/heltec_mesh_solar/variant.h

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

* delete whitespace

Update variants/nrf52840/heltec_mesh_solar/platformio.ini

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

---------

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

* Trunk

---------

Signed-off-by: Trenton VanderWert <trenton.vanderwert@gmail.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Dane Evans <dane@goneepic.com>
Co-authored-by: Austin <vidplace7@gmail.com>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Jason P <Xaositek@users.noreply.github.com>
Co-authored-by: Markus <Links2004@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
Co-authored-by: Markus <974709+Links2004@users.noreply.github.com>
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
Co-authored-by: WillyJL <me@willyjl.dev>
Co-authored-by: Tom <116762865+NomDeTom@users.noreply.github.com>
Co-authored-by: Tom <116762865+Nestpebble@users.noreply.github.com>
Co-authored-by: Jason P <applewiz@mac.com>
Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
Co-authored-by: Michael <michael.overhorst@gmail.com>
Co-authored-by: GUVWAF <thijs@havinga.eu>
Co-authored-by: Trent V. <trenton.vanderwert@gmail.com>
Co-authored-by: Quency-D <55523105+Quency-D@users.noreply.github.com>
Co-authored-by: Tom Fifield <tom@tomfifield.net>
Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com>
2025-11-27 06:18:52 -06:00
renovate[bot]
bc3ed4a7f3 Update platformio/ststm32 to v19.4.0 (#8433)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 06:16:50 -06:00
renovate[bot]
7cb7a6cd3e Update NonBlockingRTTTL to v1.4.0 (#8541)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 06:16:12 -06:00
renovate[bot]
d0c6ec28db Update INA226 to v0.6.5 (#8645)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 06:15:36 -06:00
renovate[bot]
a6d1ce2048 Update Sensirion Core to v0.7.2 (#8551)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-27 06:14:49 -06:00
github-actions[bot]
f7ae7aa2c1 Upgrade trunk (#8623)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-11-27 06:11:14 -06:00
Jonathan Bennett
9bfef80e30 Add requestFocus() in CannedMessages (#8770)
Certain actions in CannedMessages can trigger the module losing the requestFocus bit, which puts the UI into a slightly frozen state.
2025-11-27 06:01:03 -06:00
Jason P
c3a7ad2865 More GPS pin flips for devices (#8760) 2025-11-26 13:53:26 -06:00
Ben Meadors
f10aa3daa2 Fixes 2025-11-26 11:30:34 -06:00
Quency-D
06dac12a73 Swap the GPS serial port pins. (#8756)
* Swap the GPS serial port pins.

* Trunk fixes

---------

Co-authored-by: Jason P <applewiz@mac.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-11-26 11:10:21 -06:00
Ben Meadors
d60b263a00 Merge branch 'master' into develop 2025-11-26 06:24:41 -06:00
Jonathan Bennett
654abe5b2c Add support for muzi-base (#8753) 2025-11-25 18:28:06 -06:00
Thomas Göttgens
79e8fc94bc 3401 fix (#8755)
* Preliminary Thinknode M4 Support

* fix 3401 detection

* don't push unrelated work
2025-11-25 16:35:17 -06:00
Tom
486fa74549 Actions: Remove native from build_one (#8685)
* Remove native from the build, and remove the required permissions

* Delete .github/workflows/build_one_arch.yml

Its borken and not really needed. one_target is the goal.
2025-11-25 16:18:55 -05:00
Ben Meadors
66193e1776 Prevent double-registering of Rotary Encoder on TLora Pager (#8746)
* Reduce noise

* Prevent double registering of rotary encoder broker
2025-11-25 14:34:55 -06:00
Ben Meadors
bacff5c1f0 Reduce noise 2025-11-25 14:34:23 -06:00
Jason P
faa6af74af Swapping GPS pins for GPS TX/RX (#8751) 2025-11-25 13:55:28 -06:00
Jason P
81439f16d0 More quickly hide "Shutting Down" to prevent it showing on Eink sleep screen (#8749) 2025-11-25 08:59:11 -06:00
Jonathan Bennett
592a8f23db Further fix compass calibration (#8740)
* Update calibration logic for ICM20948 sensor

Initialize highest and lowest magnetic values based on sensor data readiness during calibration.

* Refactor BMX160 calibration to use magnetometer data

Update calibration logic to initialize highest and lowest values using magnetometer data.

* Add missed viable defines in ::calibrate()
2025-11-25 06:10:20 -06:00
Ben Meadors
2baa9ccbe0 Merge pull request #8741 from meshtastic/master
Master to develop
2025-11-25 04:52:54 -06:00
Jonathan Bennett
0336331411 Use LED_CHARGE and LED_PAIRING for M6 led control (#8742) 2025-11-25 09:29:35 +01:00
Jonathan Bennett
ed4a798c60 Thinknode M3 support against master (#8630)
* Add variant_shutdown() as a week function in main-nrf52.cpp

* Add Status LED module

* Add Thinknode M3 support

* Catch case of BLE disabled

* Update src/modules/StatusLEDModule.cpp

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

* Update src/modules/StatusLEDModule.cpp

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

* Update variants/nrf52840/ELECROW-ThinkNode-M3/rfswitch.h

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

* Remove unused pin

* M3 pairing LED only active for 30 seconds after state change

* Thinknode M3 shutdown work

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-24 16:35:54 -06:00
Chloe Bethel
5d7da6868e Support overriding GPS serial pins on all architectures (#8486) 2025-11-24 09:40:27 +08:00
simon-muzi
1bfa9ed4c4 Tweak OCV_ARRAY 100% voltage to take into account charger hysteresis and voltage sag after charge (#8720)
Measured voltage of fully charged battery after a few minutes of rest
2025-11-22 16:35:10 -06:00
Jonathan Bennett
b18794e98d Log error if startReceive fails in LR11x0Interface (#8718) 2025-11-22 13:54:24 -06:00
simon-muzi
f4e260e0f1 R1 Neo - Added OCV_ARRAY from measured discharge curve testing + update ADC multiplier (#8716)
* Added OCV_ARRAY from measured discharge curve testing and update ADC multiplier

The ADC resistor divider ratio is 0.6 -> multiplier should be 1/0.6 ~=1.667

We data logged a full discharge curve at constant 100mA draw over 15hours to get a realistic voltage curve for battery SoC measurements.

* Remove power.h in favor of variant.h

---------

Co-authored-by: Jason P <applewiz@mac.com>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-11-22 13:54:10 -06:00
Avi0n
14463043bd Add WisMesh Tag OCV array (#8646)
* Add WisMesh Tag OCV array

* Update 10% to 3650

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-11-22 12:03:47 -06:00
renovate[bot]
376dc7ef3a Update actions/checkout action to v6 (#8695)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-22 08:27:00 -06:00
renovate[bot]
c051c56544 Update Kongduino-Adafruit_nRFCrypto digest to 8cde718 (#8708)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-22 08:26:39 -06:00
Ben Meadors
d3976e7461 Merge pull request #8713 from meshtastic/develop
Develop to master
2025-11-22 08:23:46 -06:00
github-actions[bot]
a4c92d9fd5 Update protobufs (#8707)
Co-authored-by: caveman99 <25002+caveman99@users.noreply.github.com>
2025-11-21 20:31:12 +01:00
Ben Meadors
186cbe61bb Merge pull request #8705 from meshtastic/thinknode-m6
Add Thinknode M6
2025-11-21 05:48:16 -06:00
github-actions[bot]
0e3e8b7607 Update protobufs (#8707)
Co-authored-by: caveman99 <25002+caveman99@users.noreply.github.com>
2025-11-21 11:02:13 +01:00
Thomas Göttgens
451e52b541 fix some minor compiler warnings. Note: The 'delete' is actually safe, so we suppress the warning. 2025-11-21 10:42:15 +01:00
Thomas Göttgens
d743ba8e75 Add Thinknode M6 2025-11-21 10:14:06 +01:00
Ben Meadors
626dce8323 Merge pull request #8701 from jasonbcox/fix-exclude-pki-menuhandler
Fix MenuHandler when MESHTASTIC_EXCLUDE_PKI is defined
2025-11-20 17:07:21 -06:00
Jason B. Cox
0100eeea67 Fix MenuHandler when MESHTASTIC_EXCLUDE_PKI is defined 2025-11-20 14:20:18 -08:00
Ben Meadors
5640179ce2 Merge pull request #8698 from jasonbcox/fix-exclude-pki
Fix build when MESHTASTIC_EXCLUDE_PKI is defined
2025-11-20 15:15:01 -06:00
Jason B. Cox
066da492d5 Fix build when MESHTASTIC_EXCLUDE_PKI is defined 2025-11-20 12:26:07 -08:00
Ben Meadors
2b8806486d Merge pull request #8670 from SebKuzminsky/nrf52-watchdog-take-2
nrf52 watchdog (attempt #2)
2025-11-20 10:14:30 -06:00
Ben Meadors
9ae545918c Merge pull request #8694 from meshtastic/develop
Missed one
2025-11-20 09:57:44 -06:00
Ben Meadors
5374291c3c Merge pull request #8663 from meshtastic/master
Master --> develop
2025-11-20 09:57:01 -06:00
Ben Meadors
38b0c1588a Merge pull request #8689 from meshtastic/develop-to-master
Develop to master
2025-11-20 08:50:03 -06:00
Quency-D
f329de04c4 Add a reset pulse signal to the OLED. (#8691)
* Add a reset pulse signal to the OLED.

* The modification time is the same as that of the Adafruit_SSD1306 library.
2025-11-20 08:47:41 -06:00
Jason P
b09fa31492 Update src/graphics/draw/MenuHandler.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-20 08:07:11 -06:00
Ben Meadors
a2a0150ee8 Trunk fmt 2025-11-20 06:14:29 -06:00
Ben Meadors
9ae92724a9 Merge branch 'master' into develop-to-master 2025-11-20 05:58:57 -06:00
Jonathan Bennett
9cf369c5d0 actually respect wake_on_motion setting (#8690) 2025-11-20 05:41:32 -06:00
Ben Meadors
441a7c5b20 Merge branch 'master' into develop-to-master 2025-11-19 16:07:58 -06:00
renovate[bot]
2ca03fbf4b chore(deps): update meshtastic-esp8266-oled-ssd1306 digest to 2887bf4 (#8688)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-19 16:02:33 -06:00
Austin
ef298814f2 CI: Submit Bump Version PR against master (#8668) 2025-11-19 16:00:13 -06:00
Jason B. Cox
8d31fc5ec6 Unify uptime formatting (#8677)
* Unify uptime formatting

* Fix small label alignment item

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Jason P <applewiz@mac.com>
2025-11-19 15:59:45 -06:00
github-actions[bot]
f9433a31d1 Automated version bumps (#8684)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-11-19 12:13:28 -06:00
Sebastian Kuzminsky
7232dddd69 nrf52 wdt: pause wdt in Sleep and Halt, set timeout to 90 s
The 90 seconds wdt timeout matches the esp32 wdt timeout.
2025-11-18 11:52:08 -07:00
Sebastian Kuzminsky
10de230dba nrf52: add watchdog (#8485)
* nrf52: add watchdog

Main thread only for now.

* bump framework-arduinoadafruitnrf52 to pick up new wdt support

* clang-format the new parts of main-nrf52.cpp

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
(cherry picked from commit 83954293d8)
2025-11-18 11:52:08 -07:00
viric
d18f3f7a65 Allow deepsleep in rak4630 and make it restart well when power comes back (#7882)
* Make RAK4631 nodes power back on deep sleep

The devices will hang if the VBAT goes under 1.7V (Brown-out reset) and
they will never come back unless power supply goes completely off.

This kills unattended nodes.

Using the SystemOff the LPCOMP we can get the nodes back again when
power comes back, even if VBAT goes under 1.7V, which moreover is more
unlikely because the device is off.

* Adding support for heltec t114

And moved particularities to variant.h

* Remove old cpp comment that belongs to variant.h

It was a leftover.

* Trunk fix

---------

Co-authored-by: Tom Fifield <tom@tomfifield.net>
2025-11-18 11:23:39 -06:00
github-actions[bot]
567b8ea1c2 Automated version bumps (#8626)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-11-18 08:25:17 -06:00
omgbebebe
d39d1917ad mqtt: do not try to send packets when it disconnected (#8658) 2025-11-18 06:33:15 -06:00
Jason P
b202559d37 Add code for preserving favorites, also move to Home screen before reseting (#8647) 2025-11-18 06:33:02 -06:00
Tom
85ea22ac38 Update to Pro-micro variants (#8600)
* Update to Pro-micro variants

Schematic updated
Xtal variant removed
Extra module added to list
Extra explanation added to readme.

* Fix markdown formatting in readme.md

* Fix formatting in readme.md for RF switch section

---------

Co-authored-by: Tom <116762865+Nestpebble@users.noreply.github.com>
2025-11-18 06:31:41 -06:00
Quency-D
15257b017c Add the Heltec v4 expansion box. (#8539)
* Add the Heltec v4 expansion box.

* Change heltec-v4-oled to heltec-v4.

* Add touchscreen to I2C scanning.

* Add reset and busy pins to the ST7789.

* Ignore the touch interrupt pin and extend the sleep time to 1 hour.

* Remove the default sleep function.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-11-18 06:31:06 -06:00
Jonathan Bennett
59864dd09d Add API types, state, and log message in Debug screen. Added persistent "Connected" icon (#8576)
* Add API types, state, and log message in Debug screen

* un-goober the API state tracking

* Set the SerialConsole api_type

* Add api_type for Ethernet

* Remove API state debugging code

* Update wording for client connection states

* Improve string width for smaller screen devices

* Reserve space on navigation bar to fit link indicator

* Add persistent Connected icon to screen

* Connect System frame to ensure text doesn't overflow

---------

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>
2025-11-18 06:26:34 -06:00
Jason B. Cox
edcdb2dcb2 Cleanup unnecessary global dereferencing in CryptoEngine (#8611)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-11-18 06:25:30 -06:00
Jonathan Bennett
6c09cf9d3d Gps reset detect (#8302)
* Properly format timestamp in log message

* Better formatting of GPS_DEBUG logging in gps probe

* Reset GPS after serial speed change, and look for magic string to identify chip

* Add UC6580 to boot message detection, for Heltec Tracker

* Add L76K detect from boot string, for Heltec-v4

* Slightly more useful GPS debugging

* Back out detection of L76K via startup messages.

* Ignore PIN_GPS_RESET = -1 and rename passive_detect array.

---------

Co-authored-by: Tom Fifield <tom@tomfifield.net>
2025-11-18 15:04:44 +08:00
Ben Meadors
ef4cb2abfb If we're not client proxying and we are not connected, don't publish 2025-11-17 20:05:42 -06:00
omgbebebe
c34f94abda mqtt: do not try to send packets when it disconnected (#8658) 2025-11-17 20:01:50 -06:00
Ben Meadors
a8d1a90e16 Fix ble rssi crash (#8661)
* Fix BLE crash occuring when trying to get RSSI from Android with a bad connection handle

* Cleanup
2025-11-17 19:58:12 -06:00
Austin
501c296e75 Linux: Fix silly EPEL9 mistake (#8660) 2025-11-17 17:39:52 -06:00
omgbebebe
79a91578b7 mqtt: do not try to send packets when it disconnected (#8658) 2025-11-17 16:54:02 -06:00
Austin
ec5e79585b Don't trust the AI! (#8659)
Read the docs instead
2025-11-17 16:40:19 -06:00
Austin
438e170b03 Packaging: Add libbsd where needed (#8533) 2025-11-17 06:41:37 -06:00
Jason P
17cd83085b Remove gating for Display Options (#8651) 2025-11-16 22:05:24 -06:00
renovate[bot]
43e0c35466 chore(deps): update dorny/test-reporter action to v2.2.0 (#8637)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-16 20:00:05 -06:00
Chloe Bethel
b7bdcbe43e Address review comments 2025-11-16 17:18:27 -06:00
Chloe Bethel
df063f40ff Try to look for a config file based on the HAT vendor/product for autoconfig 2025-11-16 17:18:27 -06:00
Jonathan Bennett
6e3be132f2 Reset the calibration data back to 0 when doing a compass calibration 2025-11-16 16:20:30 -06:00
Jason P
84bb1e33a6 Add code for preserving favorites, also move to Home screen before reseting (#8647) 2025-11-16 14:18:16 -06:00
Jason P
955347bf50 Remove fixed scaling in Digital Clock (#8620)
* Update digital clock draw to auto scale to correct size; no more fixed scaling

* Static scale calcuation to improve performance

* Update src/graphics/draw/ClockRenderer.cpp

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

* Back off for width or height exceeds

* Fixes for some calcuations

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-16 08:42:51 -06:00
Dane Evans
4284fc2aec Feat/6704 neighbor info on demand (#8523)
* full thing. works

* works

* minimal changes

* roll back previous changes, move to using the alloc() overrride

* clean up comments

* format

* run clang-format manually.
Trunk may be the absolute worst formatter in existance

* format on WSL to fix trunks awfulness

* add a 3 minute cooldown to prevent messages going back and forth

* add ignoring the dummy neighbor.

* fix or.

* fix spelling, increase logging

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-11-15 19:49:46 -06:00
github-actions[bot]
034aaa376a Automated version bumps (#8626)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-11-14 06:09:25 -06:00
Jonathan Bennett
0aa11d810c Clean up GPS toggle logging
Removed redundant log warnings for GPS toggle events.
2025-11-13 12:04:39 -06:00
github-actions[bot]
4df6627ab1 Upgrade trunk (#8606)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-11-12 05:33:31 -06:00
Andrik45719
7212fb9caa Fix null pointer dereference in radio chip region check (#8613) 2025-11-11 10:00:08 -06:00
Jason B. Cox
4118e1c0f6 Cleanup unnecessary global dereferencing in CryptoEngine (#8611)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-11-10 20:19:15 -06:00
Thomas Göttgens
a62fed3289 Merge pull request #8610 from meshtastic/fix-ldro
Change RadioLib to commit zip til 7.4.1+ is released
2025-11-10 22:31:03 +01:00
Ben Meadors
e9590003f4 Only call stopNow if we're nagging (#8601) 2025-11-10 11:58:39 -06:00
Thomas Göttgens
7d2744fae0 Change RadioLib to commit zip til 7.4.1+ is released
fixes regression for SX127x chips per @GUVWAF
2025-11-10 16:16:24 +01:00
Chloe Bethel
beaebda4de stm32wl: Wrap and remove some functions that pull in large amounts of code/data to claw back even more flash space (#8609) 2025-11-10 09:08:04 -06:00
Tom
36c2178570 Update to Pro-micro variants (#8600)
* Update to Pro-micro variants

Schematic updated
Xtal variant removed
Extra module added to list
Extra explanation added to readme.

* Fix markdown formatting in readme.md

* Fix formatting in readme.md for RF switch section

---------

Co-authored-by: Tom <116762865+Nestpebble@users.noreply.github.com>
2025-11-09 09:24:03 -06:00
github-actions[bot]
1c0c6b2736 Automated version bumps (#8527)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-11-08 18:41:04 -06:00
Quency-D
602945f66b Add the Heltec v4 expansion box. (#8539)
* Add the Heltec v4 expansion box.

* Change heltec-v4-oled to heltec-v4.

* Add touchscreen to I2C scanning.

* Add reset and busy pins to the ST7789.

* Ignore the touch interrupt pin and extend the sleep time to 1 hour.

* Remove the default sleep function.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-11-08 06:50:20 -06:00
Quency-D
50f9be9a2b Add the Heltec v4 expansion box. (#8539)
* Add the Heltec v4 expansion box.

* Change heltec-v4-oled to heltec-v4.

* Add touchscreen to I2C scanning.

* Add reset and busy pins to the ST7789.

* Ignore the touch interrupt pin and extend the sleep time to 1 hour.

* Remove the default sleep function.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-11-08 06:47:24 -06:00
Benjamin Faershtein
b86827967e Drop PKI acks if there is no downlink on MQTTClientProxy (#8580)
* Discard everything if downlink isn't on

* Drop PKI packets when downlink not on
2025-11-08 06:00:38 -06:00
Benjamin Faershtein
8fe98db5dd Drop PKI acks if there is no downlink on MQTTClientProxy (#8580)
* Discard everything if downlink isn't on

* Drop PKI packets when downlink not on
2025-11-08 05:59:45 -06:00
Jonathan Bennett
531cad5e88 Add API types, state, and log message in Debug screen. Added persistent "Connected" icon (#8576)
* Add API types, state, and log message in Debug screen

* un-goober the API state tracking

* Set the SerialConsole api_type

* Add api_type for Ethernet

* Remove API state debugging code

* Update wording for client connection states

* Improve string width for smaller screen devices

* Reserve space on navigation bar to fit link indicator

* Add persistent Connected icon to screen

* Connect System frame to ensure text doesn't overflow

---------

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>
2025-11-07 15:03:56 -06:00
github-actions[bot]
b707001873 Upgrade trunk (#8552)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-11-07 05:33:54 -06:00
renovate[bot]
85afd706fd chore(deps): update meshtastic/device-ui digest to 28167c6 (#8583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-07 05:33:36 -06:00
Ben Meadors
e76013fb60 Try-fix traceroute panic (#8568) 2025-11-07 05:16:00 -06:00
Benjamin Faershtein
b25797e1b3 Discard everything if downlink isn't on (#8578) 2025-11-06 21:02:46 -06:00
Ford Jones
bdb3fb1477 Persist favourites on NodeDB reset (#8292)
* Conditionally delete favourited nodes on reset

* trunk fmt

* Fix equality check, use existing macro for role validation

* Extend favourite persistence setting to devices of all roles

* Refactor: Decoupled role/config check and set role defaults appropriately

* Use American-English spelling

* Use existing reference

* Convert reset to bool, regen protos

* Add optional arg to nodedb_reset in favor of additional device setting

* Use correct proto commit ID

* Regen protos

* Log preservation status

* Pull latest from master
2025-11-06 21:02:36 -06:00
Jonathan Bennett
7eca061f01 Bugfix: Don't toggle BLE when choosing active state (#8579) 2025-11-06 21:01:30 -06:00
Benjamin Faershtein
77e0a24838 Discard everything if downlink isn't on (#8578) 2025-11-06 21:01:15 -06:00
Ford Jones
6cad393688 Persist favourites on NodeDB reset (#8292)
* Conditionally delete favourited nodes on reset

* trunk fmt

* Fix equality check, use existing macro for role validation

* Extend favourite persistence setting to devices of all roles

* Refactor: Decoupled role/config check and set role defaults appropriately

* Use American-English spelling

* Use existing reference

* Convert reset to bool, regen protos

* Add optional arg to nodedb_reset in favor of additional device setting

* Use correct proto commit ID

* Regen protos

* Log preservation status

* Pull latest from master
2025-11-06 16:06:37 -06:00
Ben Meadors
0725b46744 Merge branch 'master' into develop 2025-11-06 07:14:51 -06:00
Mike Robbins
4d86bbafe6 addFromContact: Don't auto-favorite when CLIENT_BASE; don't update last_heard unless CLIENT_BASE (#8495)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-11-06 07:14:33 -06:00
Wessel
112b294ef6 Store hop/mqtt/transport mechanism info in S&F (#8560)
Before this, all messages received when enabling S&F server would return
Hops away: -1
2025-11-06 07:14:14 -06:00
github-actions[bot]
5ba04ade2d Update protobufs (#8566)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-11-06 07:10:57 -06:00
Mike Robbins
6a6c409b9a addFromContact: Don't auto-favorite when CLIENT_BASE; don't update last_heard unless CLIENT_BASE (#8495)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-11-06 07:10:20 -06:00
Jonathan Bennett
69db3bd11c Reject legacy text message DMs (#8562)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-11-06 06:28:13 -06:00
Wessel
7b14b173d9 Store hop/mqtt/transport mechanism info in S&F (#8560)
Before this, all messages received when enabling S&F server would return
Hops away: -1
2025-11-06 06:27:25 -06:00
Manuel
45bf2468a9 fix missing key 0 (#8564) 2025-11-05 19:32:56 -06:00
Jason P
ce2e08e0d8 Don't Favorite Nodes if our Role is CLIENT_BASE (#8558)
* Don't Favorite Nodes if our Role is CLIENT_BASE

* Update CannedMessageModule.cpp
2025-11-05 13:19:55 -06:00
Jason P
3e40d7896b Revert "nrf52: add watchdog (#8485)" (#8554)
This reverts commit 83954293d8.
2025-11-05 10:07:28 -06:00
renovate[bot]
a579a9d011 chore(deps): update adafruit pct2075 to v1.0.6 (#8548)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 15:35:14 -06:00
renovate[bot]
6b55ec6603 chore(deps): update python to v3.14.0 (#8542)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 11:36:05 -06:00
Jonathan Bennett
f2400c9dc6 Update platform-native for WIFi lib fix (#8544)
Updates the WiFi library way down in Portduino, to detect TCP connection drops
2025-11-04 11:35:44 -06:00
github-actions[bot]
0a13bcaabf Upgrade trunk (#8437)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-11-04 06:07:12 -06:00
renovate[bot]
03f69b3b77 Update RadioLib to v7.4.0 (#8456)
* fix strlcpy compile error in Ubuntu 22.04 (#8520)

* fix strlcpy error in Ubuntu 20.04

* add to native after tests

* Add support for RAK_WISMESH_TAP_V2 and RAK3401 hardware models (#8537)

* Update RadioLib to v7.4.0

---------

Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com>
Co-authored-by: Daniel.Cao <144674500+DanielCao0@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-04 06:04:56 -06:00
Daniel.Cao
3ed831b8a3 Add support for RAK_WISMESH_TAP_V2 and RAK3401 hardware models (#8537) 2025-11-04 05:53:08 -06:00
Sebastian Kuzminsky
83954293d8 nrf52: add watchdog (#8485)
* nrf52: add watchdog

Main thread only for now.

* bump framework-arduinoadafruitnrf52 to pick up new wdt support

* clang-format the new parts of main-nrf52.cpp

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-11-04 05:35:44 -06:00
Austin
91621427f1 Packaging: Add libbsd where needed (#8533) 2025-11-03 17:56:31 -06:00
Manuel
cf716fe5ef fix strlcpy compile error in Ubuntu 22.04 (#8520)
* fix strlcpy error in Ubuntu 20.04

* add to native after tests
2025-11-03 17:11:16 -06:00
Ben Meadors
8d1b9c9dce Merge pull request #8531 from meshtastic/master
Master
2025-11-03 15:56:42 -06:00
Ben Meadors
538c05ad6c Revert "ADD - heltec v4 support to device install bat (#8528)" (#8532)
This reverts commit 468247fb94.
2025-11-03 15:56:23 -06:00
Quency-D
f6370bea8f Add the identification code for the DA217 triaxial accelerometer. (#8526) 2025-11-04 07:18:52 +11:00
Melon
468247fb94 ADD - heltec v4 support to device install bat (#8528) 2025-11-04 07:14:24 +11:00
github-actions[bot]
3ae7e54681 Automated version bumps (#8527)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-11-03 10:14:08 -06:00
Dmitry Ivanishkin
0a124b7f3d Fix SHT4x detection by reading unique serial nubmer (#8525) 2025-11-03 08:17:52 -06:00
Jonathan Bennett
7def82595d Rename screen options to display options and add units chooser (#8517)
* Rename screen options to display options and add units chooser

* Add InitialSelected to DisplayUnits_menu
2025-11-02 21:30:41 -06:00
Ben Meadors
597fa0b382 Add heltec v4 to bat as well 2025-11-02 06:11:47 -06:00
Melon
b5b9dc310f Update device-install.sh to support heltec-v4 (#8509)
* Update device-install.sh

* Update device-install.sh
2025-11-02 06:10:02 -06:00
Melon
3a67204f6d Update device-install.sh to support heltec-v4 (#8509)
* Update device-install.sh

* Update device-install.sh
2025-11-02 06:09:15 -06:00
Jonathan Bennett
d1b66782d1 Hide nodes that don't have position in the distance and bearings nodelists (#8518) 2025-11-02 05:57:15 -06:00
Ben Meadors
a7796fc7b4 Fix dismiss of ext. notification (#8512)
* Dismiss all ext notifications with any input broker event

* Account for nagging
2025-11-01 21:11:36 -05:00
Xavier horwood
718fd118b0 Add IPv6 Support for esp32 (#6866)
* Update Default.h

* Update NodeDB.cpp

* Update WiFiAPClient.cpp

* Update userPrefs.jsonc

* set ipv6 to off by default

* Trunk fix

---------

Co-authored-by: Tom Fifield <tom@tomfifield.net>
2025-11-02 09:25:59 +11:00
Ben Meadors
75f7ded12c Merge pull request #8513 from meshtastic/master
Master backmerge
2025-11-01 17:17:14 -05:00
GUVWAF
bca0e1abde Fix boot on RP2040 by excluding new FreeRTOS task (#8508) 2025-11-01 16:48:04 -05:00
Jonathan Bennett
c46abe125c Skip setting up Lora GPIO lines when using a ch341 radio on native (#8506) 2025-11-01 12:45:11 -05:00
pa0lin082
7f78a624cd Add support for Bh1750 Light Sensor (#8376)
* regenerate protobuf with bh1750 TelemetrySensorType

* Added wollewald/BH1750_WE@^1.1.10 dependecy

* Added support for BH1750 during i2C detection

* Create new BH1750Sensor and added in EnvironmentTelemetry

* clean code

* Attempt to fix protobuf include

---------

Co-authored-by: Tom Fifield <tom@tomfifield.net>
2025-11-01 09:40:36 +11:00
shortwavesurfer2009
17324fa725 adjust battery curve (#8137)
Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
2025-11-01 09:39:30 +11:00
Marius Faber
001654e90a Add basic LR1121 support for T-Beam S3, full support needs #4775 fixed (#8349) 2025-11-01 09:38:37 +11:00
Erayd
16b1280804 Fix type to ensure correct alignment; saves 4B per entry (#8465) 2025-10-31 07:09:53 -05:00
Jason P
d00fda2f4d Better implementation of ExternalNotificationModule::stopNow (#8492)
* Better implementation of ExternalNotificationModule::stopNow

* Label external states turning off

* Optimize original code to actually fix issues
2025-10-31 05:55:56 -05:00
Andrew Yong
4f817d69eb fix(wio-e5): Fix LED state inversion (#8500)
Wio-E5 currently has LED appearing to be steadily on, due to incorrect LED_STATE_ON (it is actually briefly flashing off, but visually it is hard to perceive).

Wio-E5 has LED between GPIO PB5 and VCC, so LED_STATE_ON should be 0 for LED to blink correctly. With this commit, it is now flashing correctly.

Refer to schematics:

* [Wio-E5 Development Kit](https://files.seeedstudio.com/products/113990934/LoRa-E5%20Dev%20Board%20v1.0.pdf)
* [Wio-E5 mini](https://files.seeedstudio.com/products/113990939/LoRa-E5%20mini%20v1.0.pdf)

Signed-off-by: Andrew Yong <me@ndoo.sg>
2025-10-31 05:55:07 -05:00
Chloe Bethel
de83b448f9 Force stdout to be line buffered - this fixes logs ending early if meshtasticd crashes (#8499) 2025-10-31 05:54:35 -05:00
Ixitxachitl
c145be8e05 Refactor emote dimensions to 16x16 pixels (#8493)
Updated the dimensions of various emotes in emotes.h from 30x30 or 25x25 to 16x16 pixels for consistency and optimization. Added new emotes including heart_smile, Heart_eyes, and others, all with the same 16x16 size. This change improves memory usage and aligns with the design specifications for smaller emotes.
2025-10-31 21:20:29 +11:00
Jonathan Bennett
756efa7f00 Thinknode M5 ADC_MULTIPLIER to actually hit 100% charge (#8489) 2025-10-30 06:23:11 -05:00
Jonathan Bennett
0dfa11a909 Add missed debug log line in RF95 Interface (#8490) 2025-10-30 14:35:54 +11:00
Jonathan Bennett
c330bfe848 Turn the e-ink backlight on for any brightness value over 0 (#8481) 2025-10-29 06:46:50 -05:00
Clive Blackledge
28f53d132a refactor: change node count variables from uint8_t to uint16_t (#8478)
This is a non-breaking change that increases the internal representation
of node counts from uint8_t (max 255) to uint16_t (max 65535) to support
larger mesh networks, particularly on ESP32-S3 devices with PSRAM.

Changes:
- NodeStatus: numOnline, numTotal, lastNumTotal (uint8_t -> uint16_t)
- ProtobufModule: numOnlineNodes (uint8_t -> uint16_t)
- MapApplet: loop counters changed to size_t for consistency with getNumMeshNodes()
- NodeStatus: Fixed log format to use %u for unsigned integers

Note: Default class methods keep uint32_t for numOnlineNodes parameter
to match the public API and allow flexibility, even though internal node
counts use uint16_t (max 65535 nodes).

This change does NOT affect protobuf definitions, maintaining wire
compatibility with existing clients and devices.
2025-10-28 15:32:08 -05:00
renovate[bot]
7d3e529b2f Update node to v24 (#8476)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-29 07:16:09 +11:00
Erayd
f045ca2303 Fix type to ensure correct alignment; saves 4B per entry (#8465) 2025-10-27 06:05:59 -05:00
Tom Fifield
b6830a68a0 Migrate test workflow to use Node 24 (#8466)
Node 24 is now the common version amoungst all of our actions.
2025-10-27 19:47:34 +11:00
renovate[bot]
dd51de85f3 Update GitHub Artifact Actions (#8443)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 19:42:32 +11:00
Jason P
580fa292ac Address longName wrapping (#8441)
* Address longName wrapping

* Update src/graphics/draw/NodeListRenderer.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-10-25 07:12:59 -05:00
GUVWAF
664d17c519 Revert "Revert "develop --> Master" (#8244)" (#8450)
This reverts commit 5bcc47dddb.
2025-10-25 06:59:01 -05:00
Ben Meadors
13c4c2037d Merge pull request #8444 from meshtastic/master
Master --> develop
2025-10-24 21:06:29 -05:00
Tom Fifield
95d3ecb239 Merge branch 'develop' into master 2025-10-25 10:38:02 +11:00
Tom Fifield
799cf0e8b3 Master --> develop (#8436)
* Issue: #7944 External notification module: Adjusted default nag timeout to 15s (from 60s) (#7946)

* External notification module: Adjusted default nag timeout to 5s (from 60s)

* Change nag to 15s

---------

Co-authored-by: Tom Fifield <tom@tomfifield.net>

* Add support for RAK WISMESH TAP V2 by enabling SDCARD_CS pin during deep sleep (#8429)

* Upgrade trunk (#8369)

Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>

* Don't assign negative SNR to unsigned int type

SNR-based contention windows are broken on systems with 64-bit long integers.
Fixes #8430

* Allow vibra or buzzer only notifications to obey cutoff (#8342)

* Allow vibra or buzzer only notifications to obey cutoff

* Update src/modules/ExternalNotificationModule.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>

* InkHUD crash fix when nodes get deleted from NodeDB (#8428)

* InkHUD crash fix

* trunk fix

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Onyx Clawe <58921814+OnyxClawe@users.noreply.github.com>
Co-authored-by: Daniel.Cao <144674500+DanielCao0@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
Co-authored-by: korbinianbauer <64415847+korbinianbauer@users.noreply.github.com>
Co-authored-by: Jason P <applewiz@mac.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: HarukiToreda <116696711+HarukiToreda@users.noreply.github.com>
2025-10-24 10:37:38 +11:00
HarukiToreda
35fa418739 InkHUD crash fix when nodes get deleted from NodeDB (#8428)
* InkHUD crash fix

* trunk fix
2025-10-23 11:55:24 -05:00
Ben Meadors
585d9d36a8 Merge pull request #8432 from korbinianbauer/develop
Don't use unsigned integer type for negative SNR value
2025-10-23 11:54:44 -05:00
Jason P
b682ab3967 Allow vibra or buzzer only notifications to obey cutoff (#8342)
* Allow vibra or buzzer only notifications to obey cutoff

* Update src/modules/ExternalNotificationModule.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-10-23 11:54:05 -05:00
Ben Meadors
49b9d5151d Merge branch 'master' into develop 2025-10-23 11:35:33 -05:00
korbinianbauer
3f8707cafe Merge branch 'meshtastic:develop' into develop 2025-10-23 16:15:30 +02:00
korbinianbauer
39780656ef Don't assign negative SNR to unsigned int type
SNR-based contention windows are broken on systems with 64-bit long integers.
Fixes #8430
2025-10-23 16:15:12 +02:00
github-actions[bot]
153cf65214 Upgrade trunk (#8369)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-10-23 06:14:14 -05:00
GUVWAF
07d354fa02 Move airtime calculation to when Tx is complete (#8427) 2025-10-23 05:42:36 -05:00
Daniel.Cao
f4e93b4a2d Add support for RAK WISMESH TAP V2 by enabling SDCARD_CS pin during deep sleep (#8429) 2025-10-23 05:41:24 -05:00
Onyx Clawe
18c4956aba Issue: #7944 External notification module: Adjusted default nag timeout to 15s (from 60s) (#7946)
* External notification module: Adjusted default nag timeout to 5s (from 60s)

* Change nag to 15s

---------

Co-authored-by: Tom Fifield <tom@tomfifield.net>
2025-10-22 21:02:14 +11:00
Ford Jones
15ee1c2819 Include RSSI in rangetest csv (#8395)
* Include RSSI in rangetest csv

* Fix typo

* Preserve csv column order

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-22 20:08:17 +11:00
Ben Meadors
f4ff210311 Merge pull request #8412 from meshtastic/develop
Develop to Master
2025-10-20 05:56:58 -05:00
Ben Meadors
26747038bb Merge pull request #8397 from meshtastic/InkHUD-Fixes
InkHUD Map improvements
2025-10-20 05:56:03 -05:00
Ben Meadors
8e082686a3 Merge pull request #8400 from Stary2001/stm32-dynamic-queues
Make packet pool dynamic again on STM32 as a workaround
2025-10-20 05:55:46 -05:00
Ben Meadors
871986d183 Merge pull request #8404 from compumike/compumike/fix-nimble-bluetooth-process-fromPhone-before-toPhone
Fix NimbleBluetooth: process fromPhoneQueue (phone->radio) before toPhoneQueue (radio->phone)
2025-10-20 05:55:24 -05:00
Ben Meadors
821d8aa15d Merge branch 'develop' into compumike/fix-nimble-bluetooth-process-fromPhone-before-toPhone 2025-10-20 05:54:30 -05:00
Jason P
c4656dacf7 Remove "Phone GPS" in order to correct GPS reporting (#8407)
* Removing Phone GPS reporting for the moment
2025-10-19 20:14:30 -04:00
Jonathan Bennett
64d92679d0 Merge branch 'develop' into compumike/fix-nimble-bluetooth-process-fromPhone-before-toPhone 2025-10-19 17:23:04 -05:00
Chloe Bethel
b5aa16bade Add a banner on startup when DEBUG_MUTE is enabled (#8402) 2025-10-20 08:23:12 +11:00
HarukiToreda
c740550d16 Merge branch 'develop' into InkHUD-Fixes 2025-10-19 16:27:31 -04:00
HarukiToreda
cb3ce1b1a8 proper centering and rounder hops labels 2025-10-19 16:25:53 -04:00
HarukiToreda
2ad52812c0 Update src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp
better for clarity

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-19 15:12:03 -04:00
HarukiToreda
5b9563a357 Update src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp
makes sense, applying did not cause any visible issues.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-19 15:11:06 -04:00
Mike Robbins
126954c2ed NimbleBluetooth: reuse BLE_HS_CONN_HANDLE_NONE instead of creating a different constant to represent no connection 2025-10-19 11:10:14 -04:00
Mike Robbins
f6eede8597 NimbleBluetooth: process fromPhoneQueue before toPhoneQueue (fixes bug with 0-length reads during config phase) 2025-10-19 11:00:47 -04:00
Ben Meadors
6f2241751e Merge pull request #8401 from meshtastic/master
Backmerge Master
2025-10-19 07:31:03 -05:00
Ben Meadors
1d4134c08c Merge branch 'develop' into stm32-dynamic-queues 2025-10-19 07:30:01 -05:00
github-actions[bot]
ffb168be00 Update protobufs (#8398)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-10-19 05:53:06 -05:00
Ben Meadors
27dc3be14a Merge branch 'develop' 2025-10-19 05:49:50 -05:00
Mike Robbins
f2a63faddd Fix NimbleBluetooth reliability and performance (#8385)
* Initial work to get NimbleBluetooth working reliably, and cross-task mutexes cleaned up

* Pre-fill toPhoneQueue when safe (during config/nodeinfo): runOnceToPhoneCanPreloadNextPacket

* Handle 0-byte responses breaking clients during initial config phases

* requestLowerPowerConnection

* PhoneAPI: onConfigStart and onConfigComplete callbacks for subclasses

* NimbleBluetooth: switch to high-throughput BLE mode during config, then lower-power BLE mode for steady-state

* Add some documentation to NimbleBluetooth.cpp

* make cppcheck happier

* Allow runOnceHandleToPhoneQueue to tell runOnce to shouldBreakAndRetryLater, so we don't busy-loop forever in runOnce

* Gating some logging behind DEBUG_NIMBLE_ON_READ_TIMING ifdef again; bump retry count

* Add check for connected state in NimBLE onRead()

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-10-19 05:32:58 -05:00
Chloe Bethel
d2403437ff Make packet pool dynamic again on STM32 as a workaround 2025-10-19 10:48:19 +01:00
igorka48
05c176c16a Added support for SugarCube device (#8187)
* Added support for SugarCube device

* Update variants/esp32/sugarcube/platformio.ini

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

* added buzzer pin

* Apply PR comments

* Fix MR comments

---------

Co-authored-by: Austin <vidplace7@gmail.com>
2025-10-19 20:48:06 +11:00
HarukiToreda
7afc6ef833 trunk 2025-10-19 03:47:02 -04:00
HarukiToreda
68e739359f cleanup 2025-10-19 03:36:25 -04:00
HarukiToreda
2357ea0042 Clearer hop markers for inkHUD map 2025-10-19 02:48:55 -04:00
HarukiToreda
cbdbaf62f1 Merge branch 'develop' into InkHUD-Fixes 2025-10-19 00:05:24 -04:00
github-actions[bot]
e1c259ae32 Update protobufs (#8396)
Co-authored-by: fifieldt <1287116+fifieldt@users.noreply.github.com>
2025-10-18 18:21:24 -05:00
HarukiToreda
b4dea63f44 Gatting off BaseUI code from screenless devices and InkHUD (#8384) 2025-10-19 09:00:35 +11:00
HarukiToreda
30022c9377 Fixe battery voltage to show missing decimals (#8386) 2025-10-19 09:00:14 +11:00
Tom Fifield
0283e0658b Master --> develop (#8394)
* Update exempt labels for stale bot workflow

Adds triaged and backlog to the list of exempt labels.

* Update meshtastic/web to v2.6.7 (#8381)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update DFRobot_RTU to v1.0.6 (#8387)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update mcr.microsoft.com/devcontainers/cpp Docker tag to v2 (#8375)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* manual merge stale bot config (#8392)

* Board support:  RAK3401+RAK13302 1-watt  (#8140)

* Add RAK3401 variant files

* Add SPI configuration for RAK3401 and RAK13302 variants

* Refactor SPI pin configuration and clean up variant definitions for RAK3401

* Add TX_GAIN_LORA for RAK13302 Power Amp

* Fix merge

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Tom Fifield <tom@tomfifield.net>
Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com>

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Daniel.Cao <144674500+DanielCao0@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com>
2025-10-19 08:54:56 +11:00
Tom Fifield
4f142e6766 Merge branch 'develop' into master 2025-10-19 08:54:31 +11:00
Daniel.Cao
af8407aca9 Board support: RAK3401+RAK13302 1-watt (#8140)
* Add RAK3401 variant files

* Add SPI configuration for RAK3401 and RAK13302 variants

* Refactor SPI pin configuration and clean up variant definitions for RAK3401

* Add TX_GAIN_LORA for RAK13302 Power Amp

* Fix merge

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Tom Fifield <tom@tomfifield.net>
Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com>
2025-10-19 08:53:39 +11:00
Tom Fifield
e5d67310d6 Master ---> Develop (#8391)
* Update exempt labels for stale bot workflow

Adds triaged and backlog to the list of exempt labels.

* Update meshtastic/web to v2.6.7 (#8381)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update DFRobot_RTU to v1.0.6 (#8387)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* Update mcr.microsoft.com/devcontainers/cpp Docker tag to v2 (#8375)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* manual merge stale bot config (#8392)

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-19 08:52:56 +11:00
Tom Fifield
47df9d4a79 Merge branch 'develop' into master 2025-10-19 08:52:29 +11:00
Tom Fifield
4df79374b0 manual merge stale bot config (#8392) 2025-10-19 08:52:03 +11:00
renovate[bot]
0bfc342b48 Update mcr.microsoft.com/devcontainers/cpp Docker tag to v2 (#8375)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-18 19:20:07 +11:00
renovate[bot]
d9905f3c31 Update DFRobot_RTU to v1.0.6 (#8387)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-18 19:17:34 +11:00
HarukiToreda
ee2ed0a8fb Fixe battery voltage to show missing decimals 2025-10-17 23:26:47 -04:00
renovate[bot]
acab814b6f Update meshtastic/web to v2.6.7 (#8381)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-18 09:14:34 +11:00
Tom Fifield
073c35c782 Update exempt labels for stale bot workflow
Adds triaged and backlog to the list of exempt labels.
2025-10-17 09:01:51 +11:00
Tom Fifield
32ebc70bca Update exempt labels for stale bot configuration
Adds triaged and backlog to the list of exempt labels.
2025-10-17 09:01:14 +11:00
Jonathan Bennett
5953b4704e Force CannedMessages to another node to be a PKI DM (#8373) 2025-10-16 14:01:04 -05:00
Ben Meadors
a34c584028 Merge branch 'master' into develop 2025-10-16 06:08:54 -05:00
Ben Meadors
865b46ceef Ignore MQTT Client Proxy messages while not in sendpackets state (#8358) 2025-10-16 06:07:38 -05:00
github-actions[bot]
cd3b31c5da Upgrade trunk (#8340)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-10-16 08:56:39 +11:00
renovate[bot]
51b3b937dc Update actions/setup-node action to v6 (#8339)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-16 08:55:11 +11:00
Paweł Czaplewski
ec5a54c523 bind python version to 3.13 (#8362) 2025-10-16 08:19:47 +11:00
Jonathan Bennett
4e0a4cc45f Log the lora frequency error when receiving a packet. (#8343) 2025-10-15 06:34:28 -05:00
Mike Weaver
858e8c6fef portduino, handle sdl2 builds (#8355)
fix linux native build by adding sdl2 libraries
2025-10-15 06:15:57 -05:00
Clive Blackledge
a6df18e60a Guarding PhoneAPI node-info staging with mutex to prevent BLE future foot-gun (#8354)
* Eliminating foot-gun and placing Phone NodeInfo into a mutex

* Swapping over to concurrency::Lock from mutex
2025-10-15 06:08:06 -05:00
Erayd
9a8aeb25ab Add a general-purpose packet cache (#8341)
* Add general-purpose packet cache

This commit adds a caching system that will save packet data in a much
more compact form than the regular MeshPacket protobuf. It cannot be
worked with directly to the same degree (although the packet header is
available), but consumes *much* less memory, and as a result can be used
to temporarily store large numbers of packets.

Cached packets can be retrieved either by their (from, id) tuple, or by
their hash.

This cache is a pre-requisite for the upcoming packet replay feature.

* Remove debug initialiser

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

* Fix ordering

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

* Add missing size assignment

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

* Add comments for hash & bucket macros

* Make it clear that this field stores a map of the original data

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-14 19:22:45 -05:00
renovate[bot]
e8f4d07e9f Update meshtastic/device-ui digest to 19b7855 (#8346)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-14 15:45:33 -05:00
Ben Meadors
034d2dd025 Merge pull request #8333 from NomDeTom/patch-3
Update stale_bot.yml
2025-10-14 06:40:54 -05:00
Tom
b8bfed2810 return to 45 days and put a closure message. 2025-10-14 12:37:40 +01:00
Ben Meadors
dbb439f121 Merge pull request #8337 from meshtastic/develop
Develop to master merge
2025-10-13 20:51:52 -05:00
Ben Meadors
c4d7ad2190 Kill github actions script 2025-10-13 19:56:39 -05:00
Ben Meadors
5814f3e7d2 Revert "Fix Station G2 Lora Power Settings (#8273)" (#8332)
This reverts commit 05edcc5d6c.
2025-10-13 17:17:32 -05:00
Tom
910fe911f8 Update stale_bot.yml 2025-10-13 20:12:45 +01:00
Tom
9ab9650248 Update stale_bot.yml
Extend stale period to 60 days, and added a message on stale marking.
2025-10-13 20:04:10 +01:00
Jason P
37a0f774a2 Fix multitude of warnings during builds (#8331) 2025-10-13 12:51:27 -05:00
Markus
a71b47b5bb rework sensor instantiation to saves memory by removing the static allocation (#8054)
* rework I2C sensor init

the goal is to only instantiate sensors that are pressend to save memory.
side effacts:
 - easyer sensor integration (less C&P code)
 - nodeTelemetrySensorsMap can be removed when all devices are migrated

* add missing ifdef

* refactor a bunch of more sensors

RAM -816
Flash -916

* fix build for t1000

* refactor more sensors

RAM -192
Flash -60

* improve error handling

Flash -112

* fix build

* fix build

* fix IndicatorSensor

* fix tracker-t1000-e build

not sure what magic is used but it works

* Apply suggestion from @Copilot

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

* Apply suggestion from @Copilot

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

* Apply suggestion from @Copilot

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

* Update src/modules/Telemetry/Sensor/DFRobotGravitySensor.h

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

* Fix

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-13 11:09:33 -05:00
Steven Wu
9df5aa8c70 Fix can not detect battery status while using INA226 (#8330)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-13 08:15:21 -05:00
Tom Fifield
130833b5be Fix erroneous limiting of power in Ham Mode (#8322)
Ham Mode ignores region regulatory limits, so regardless of whether
we set a single TX_GAIN_LORA or an array with a non-linear PA,
we shouldn't limit the power.

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-13 07:50:57 -05:00
Dirk Mueller
fe6509a0f2 Avoid exceeding allocated buffers when doing MQTT proxying (#8320)
the topic length could be longer than 65 characters. similarly for the
payload.

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-13 06:57:21 -05:00
github-actions[bot]
1212c2c11b Upgrade trunk (#8326)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-10-13 06:32:21 -05:00
Clive Blackledge
fcaa168d2d Ble reconnect prefetch bug fix, plus some speed enhancements (#8324)
* Fixing bluetooth reconnects and adding performance

* Added comments
2025-10-13 06:32:05 -05:00
github-actions[bot]
e24e2ccf62 Upgrade trunk (#8245)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-10-12 08:25:34 -05:00
l0g-lab
7537d28419 Nodelist: choice of long or short name (#7926)
* update to use long names for pager

* remove duplicate

* add menu item

* fix after conflict

* menu name change. use sanitizeString

* fix formatting issue. should pass trunk now.

* remove auto-generated protobufs

* remove log, add tdeck, improvements.

---------

Co-authored-by: l0g-lab <l0g-lab@users.noreply.github.com>
Co-authored-by: Tom Fifield <tom@tomfifield.net>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-12 08:25:15 -05:00
Ben Meadors
26f25069dd Merge branch 'master' into develop 2025-10-12 07:46:09 -05:00
renovate[bot]
5eeffdb290 chore(deps): update meshtastic/device-ui digest to 3fb7c0e (#8291)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-12 07:44:54 -05:00
Ben Meadors
5d71776527 Merge pull request #8317 from meshtastic/master
Master backmerge
2025-10-12 07:41:04 -05:00
Ben Meadors
661e596dbb Fix muted channel compile errors after protobuf move (#8316) 2025-10-12 07:39:23 -05:00
Ben Meadors
a6732682de Opt in to telemetry going forward (#8059) 2025-10-12 06:30:17 -05:00
Jason P
f0126d44e2 More BaseUI Frame Visibility Toggles (#8252)
* Add Power and Environmental Telemetry Hide/Show

* Allow Power and Telemetry Frames even if module disabled

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-12 06:28:23 -05:00
Clive Blackledge
fb08e17c39 Increase bluetooth 5.0 PHY speed and MTU on esp32_s3 (#8261)
* Increase Bluetooth speed to 2MB, increase MTU

* Adding esp32c6

* trunk fmt..
2025-10-12 05:35:00 -05:00
Tom Fifield
11aff46af1 Remove T1000E GPS startup delay sequence (#8236)
8 months ago, when this was added to the code, the GPS probe code
was still a little flaky.

Particularly after #6114 and #6116 were added, reliability improved
for all devices as we were sending fewer calls on the bus.

Today, the T1000E is the only Meshtastic device that regularly takes
2, 3, or 4 attempts to be detected via the probe code.

Removing these lines, on my T1000E, results in the AG3335 being
detected immediately.

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-10-12 05:34:34 -05:00
github-actions[bot]
cb11e6b720 Update protobufs (#8305)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-10-12 05:34:00 -05:00
Jonathan Bennett
464663b496 GPS_POWER_TOGGLE no longer has a function, so purge (#8312) 2025-10-12 05:33:34 -05:00
Austin
981d058e9f Actions: CI docker with a fancy matrix (#8253) 2025-10-11 15:56:59 -05:00
Ben Meadors
9056915e7b Double the number of bluetooth bonds NimBLE will store (from 3 to 6) (#8296) 2025-10-11 11:31:42 -05:00
thebentern
554112ceb5 Automated version bumps 2025-10-11 11:31:42 -05:00
renovate[bot]
29458cd8c4 Update XPowersLib to v0.3.1 (#8303)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-11 11:31:42 -05:00
Jonathan Bennett
8bf32dc042 Attach an interrupt to EXT_PWR_DETECT if present, and force a screen redraw on a power change. 2025-10-11 11:31:42 -05:00
Ben Meadors
73cadce581 Fix BLE stateful issues (#8287) 2025-10-11 11:31:42 -05:00
renovate[bot]
eee80ce636 chore(deps): update github/codeql-action action to v4 (#8250)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-11 11:31:42 -05:00
Tom Fifield
30d6962e79 Fix Station G2 Lora Power Settings (#8273)
* Force coverage tests to run in simulation mode

* Revert "Force coverage tests to run in simulation mode"

This reverts commit e4ec719e6f.

* Fix Station G2 Lora Power Settings

In #8107 we introduced the ability to specify gain values for
non-linear power amplifiers.

This patch adds appropriate values for the Station G2, based on
the table at https://wiki.uniteng.com/en/meshtastic/station-g2#summary-for-lora-power-amplifier-conduction-test

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

---------

Co-authored-by: Austin Lane <vidplace7@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-11 11:31:42 -05:00
Ben Meadors
f99747180e NimBLE speedup (#8281)
* Remove status polling code in NimBLE

* Goober

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-10-11 11:31:42 -05:00
renovate[bot]
91d928d4c5 Update meshtastic/device-ui digest to 6d8cc22 (#8275)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-11 11:31:42 -05:00
Andrew Yong
64bfe73c06 fix: Move #include "variant.h" to top of file (fixes #8276) (#8278)
* Force coverage tests to run in simulation mode

* Revert "Force coverage tests to run in simulation mode"

This reverts commit e4ec719e6f.

* fix: Move `#include "variant.h"` to top of file (fixes #8276)

The original line being further down the file causes any #ifdef/defined() checks for definitions in variant.h to silently skip.

This was noticed when `USE_GC1109_PA` in Heltec v4 and Heltec Wireless Tracker failed to correct program TX_GAIN_LORA, but will also affect any variant.h-dependent configurations in this file, if they would have been defined above where the `#include` previously was.

---------

Co-authored-by: Austin Lane <vidplace7@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-11 11:31:42 -05:00
renovate[bot]
fca5343460 Update python Docker tag to v3.14 (#8255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-11 11:31:42 -05:00
Austin
cafb007ec4 mDNS: Advertise pio_env (for OTA scripts) (#8298) 2025-10-11 10:30:47 -05:00
Ben Meadors
694b669eb7 Double the number of bluetooth bonds NimBLE will store (from 3 to 6) (#8296) 2025-10-11 10:30:33 -05:00
Ben Meadors
7899340131 Merge pull request #8304 from meshtastic/create-pull-request/bump-version
Bump release version
2025-10-11 10:29:46 -05:00
thebentern
0bb1c1fe6f Automated version bumps 2025-10-11 15:27:34 +00:00
renovate[bot]
05febc25e1 Update XPowersLib to v0.3.1 (#8303)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-11 17:12:10 +11:00
Jonathan Bennett
e5a2ce54e7 Attach an interrupt to EXT_PWR_DETECT if present, and force a screen redraw on a power change. 2025-10-09 19:07:20 -05:00
Ben Meadors
45f15b8fe6 Fix BLE stateful issues (#8287) 2025-10-09 12:44:00 -05:00
renovate[bot]
2a14696525 chore(deps): update github/codeql-action action to v4 (#8250)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-09 09:32:41 +11:00
Tom Fifield
05edcc5d6c Fix Station G2 Lora Power Settings (#8273)
* Force coverage tests to run in simulation mode

* Revert "Force coverage tests to run in simulation mode"

This reverts commit e4ec719e6f.

* Fix Station G2 Lora Power Settings

In #8107 we introduced the ability to specify gain values for
non-linear power amplifiers.

This patch adds appropriate values for the Station G2, based on
the table at https://wiki.uniteng.com/en/meshtastic/station-g2#summary-for-lora-power-amplifier-conduction-test

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

---------

Co-authored-by: Austin Lane <vidplace7@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-08 16:03:26 -05:00
Ben Meadors
828e11cc48 NimBLE speedup (#8281)
* Remove status polling code in NimBLE

* Goober

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-10-08 14:16:57 -05:00
renovate[bot]
adae68fbfe Update meshtastic/device-ui digest to 6d8cc22 (#8275)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-08 11:34:11 -05:00
Andrew Yong
7822f28152 fix: Move #include "variant.h" to top of file (fixes #8276) (#8278)
* Force coverage tests to run in simulation mode

* Revert "Force coverage tests to run in simulation mode"

This reverts commit e4ec719e6f.

* fix: Move `#include "variant.h"` to top of file (fixes #8276)

The original line being further down the file causes any #ifdef/defined() checks for definitions in variant.h to silently skip.

This was noticed when `USE_GC1109_PA` in Heltec v4 and Heltec Wireless Tracker failed to correct program TX_GAIN_LORA, but will also affect any variant.h-dependent configurations in this file, if they would have been defined above where the `#include` previously was.

---------

Co-authored-by: Austin Lane <vidplace7@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-08 11:33:50 -05:00
renovate[bot]
d332dfa19b Update python Docker tag to v3.14 (#8255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-08 17:33:39 +11:00
Ben Meadors
fe2e2753aa Merge branch 'master' into develop 2025-10-07 17:49:34 -05:00
Ben Meadors
fcb1d64eb9 Bloop 2025-10-07 17:47:08 -05:00
Ben Meadors
0c2673ee2f Mercy 2025-10-07 14:32:36 -05:00
Ben Meadors
9c5513dcfe Merge remote-tracking branch 'origin/master' into develop 2025-10-07 13:50:59 -05:00
Austin
74e6723ad9 Force coverage tests to run in simulation mode (#8251)
* Force coverage tests to run in simulation mode

* Revert "Force coverage tests to run in simulation mode"

This reverts commit e4ec719e6f.

* Force coverage tests to run in simulation mode

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-07 13:14:19 -05:00
Ben Meadors
e8e8ee0993 Revert "Force coverage tests to run in simulation mode"
This reverts commit e4ec719e6f.
2025-10-07 12:04:50 -05:00
Ben Meadors
a7f15097da Merge pull request #8249 from vidplace7/fix-pio-test
Force coverage tests to run in simulation mode
2025-10-07 12:00:51 -05:00
Austin Lane
e4ec719e6f Force coverage tests to run in simulation mode 2025-10-07 12:54:02 -04:00
Ben Meadors
9b7b8ffb21 Merge pull request #8247 from meshtastic/develop
Develop to master
2025-10-07 06:26:28 -05:00
szlifier
f0e4ea7664 Add SHT4x serial number for detection (#8222)
SHT4X chip recognized as SHT31, registerValue that stores first bytes of chip's serial number did not mach the chip.
Added a serial number match for SHT40 found in a SONOFF SNZB-02P.
2025-10-07 06:25:38 -05:00
Tom Fifield
468b40e8db Wait until after GPS lock hold before updating position, if we can. (#8064)
* Wait until after GPS lock hold before updating position, if we can.

After the recent patch, we hold lock for a bit before updating the position.
The positions that come in after the hold are genuinely better positions
 than what we've been doing before. However, they only come 20 seconds
 after we've got lock.

Previously, we would update the local position as soon as we got a lock as well
as at the end of the hold, since a hold was not always possible.
With this patch, if the settings allow, we should skip that first local position update.

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

* Fix falling edge handling.

* spelling

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

* Congeal lock handling

* Add named constants

* define unit to avoid confusion

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

* ifdef, not if.

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

* Add handling for when we first turn on.

* Don't run if not active

* Reset fixhold

* Logic fixes

* Add path for ACTIVE--> IDLE --> ACTIVE

Previously we only covered HARDSLEEP --> ACTIVE.

* Change hold time to gps_update_interval - 10s

* Update comment

* Add extra buffer to avoid re-starting hold

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-07 06:24:09 -05:00
Ben Meadors
bd9076b740 Remove risky change 2025-10-07 06:14:35 -05:00
Chloe Bethel
81a5aeff74 Fix serial pins for Ebyte E77 MBL board (#8246)
Also move RAK3172 and new EBYTE_E77_MBL define to variant.h, as this makes VSCode know about the defines properly...

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-07 06:11:26 -05:00
Ben Meadors
f13e7c20ba Merge branch 'master' into develop 2025-10-07 06:08:18 -05:00
Tom Fifield
5bcc47dddb Revert "develop --> Master" (#8244) 2025-10-07 06:00:09 -05:00
Jonathan Bennett
668cc9fd64 Do slightly better at threading the search for GPS hardware (#8240)
* Do slightly better at threading the search for GPS hardware

* Formatting and minor logic fix

* Remove now-spam GPS Log messages

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2025-10-07 05:58:39 -05:00
renovate[bot]
1d5b343836 Update meshtastic/device-ui digest to e564d78 (#8235)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 16:56:13 +11:00
Tom Fifield
8023f475ee Merge branch 'develop' into master 2025-10-07 16:40:44 +11:00
Jonathan Bennett
b696e083f3 Log antispam (#8241)
* less power spam

* Don't warn about the first 4 GPS checksum failures
2025-10-07 16:37:04 +11:00
renovate[bot]
b214f09ca1 Update meshtastic/web to v2.6.6 (#7583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 16:34:00 +11:00
Tom Fifield
68a2c4adda Run Integration test in simulator mode (#8232) (#8242)
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2025-10-07 16:11:36 +11:00
Tom Fifield
518680514f Actions: Simplify matrices, cleanup build_one_* (#8218) (#8239)
Co-authored-by: Austin <vidplace7@gmail.com>
2025-10-07 13:37:13 +11:00
Austin
fc1737c949 Actions: Simplify matrices, cleanup build_one_* (#8218) 2025-10-07 11:58:00 +11:00
Jonathan Bennett
735784e6e4 Run Integration test in simulator mode (#8232) 2025-10-06 13:00:44 -05:00
renovate[bot]
87e3540f48 Update meshtastic/device-ui digest to f920b12 (#8234)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 12:59:50 -05:00
renovate[bot]
329a494ce2 Update meshtastic-ArduinoThread digest to b841b04 (#8233)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 12:59:40 -05:00
Ben Meadors
627c0145e7 Centralize getNodeId and fix references to owner.id (#8230) 2025-10-06 07:56:27 -05:00
github-actions[bot]
036a58735e Upgrade trunk (#8229)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-10-06 05:50:16 -05:00
Ben Meadors
d708ed5908 Merge pull request #8215 from meshtastic/develop
Develop -> Master
2025-10-05 06:11:32 -05:00
402 changed files with 42630 additions and 14171 deletions

View File

@@ -51,7 +51,7 @@ for f in .clusterfuzzlite/*_fuzzer.cpp; do
fuzzer=$(basename "$f" .cpp)
cp -f "$f" src/fuzzer.cpp
pio run -vvv --environment "$PIO_ENV"
program="$PLATFORMIO_WORKSPACE_DIR/build/$PIO_ENV/program"
program="$PLATFORMIO_WORKSPACE_DIR/build/$PIO_ENV/meshtasticd"
cp "$program" "$OUT/$fuzzer"
# Copy shared libraries used by the fuzzer.

View File

@@ -1,7 +1,7 @@
# trunk-ignore-all(terrascan/AC_DOCKER_0002): Known terrascan issue
# trunk-ignore-all(hadolint/DL3008): Do not pin apt package versions
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
FROM mcr.microsoft.com/devcontainers/cpp:1-debian-12
FROM mcr.microsoft.com/devcontainers/cpp:2-debian-13
USER root

View File

@@ -8,7 +8,7 @@
"features": {
"ghcr.io/devcontainers/features/python:1": {
"installTools": true,
"version": "latest"
"version": "3.14"
}
},
"customizations": {

View File

@@ -2,4 +2,5 @@
self-hosted-runner:
# Labels of self-hosted runner in array of strings.
labels:
- arctastic
- test-runner

View File

@@ -76,7 +76,7 @@ runs:
done
- name: PlatformIO ${{ inputs.arch }} download cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ~/.platformio/.cache
key: pio-cache-${{ inputs.arch }}-${{ hashFiles('.github/actions/**', '**.ini') }}
@@ -100,9 +100,9 @@ runs:
id: version
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}
overwrite: true
path: |
${{ inputs.artifact-paths }}

View File

@@ -5,7 +5,7 @@ runs:
using: composite
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
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@v5
uses: actions/checkout@v6
with:
submodules: recursive
path: meshtasticd
@@ -64,7 +64,7 @@ jobs:
PKG_VERSION: ${{ steps.version.outputs.deb }}
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
overwrite: true

View File

@@ -18,9 +18,12 @@ permissions: read-all
jobs:
pio-build:
name: build-${{ inputs.platform }}
runs-on: ubuntu-24.04
# Use 'arctastic' self-hosted runner pool when building in the main repo
runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }}
outputs:
artifact-id: ${{ steps.upload.outputs.artifact-id }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}
@@ -53,14 +56,29 @@ jobs:
ota_firmware_source: ${{ steps.ota_dir.outputs.src || '' }}
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
- name: Echo manifest from release/firmware-*.mt.json to job summary
if: ${{ always() }}
env:
PIO_ENV: ${{ inputs.pio_env }}
run: |
echo "## Manifest: \`$PIO_ENV\`" >> $GITHUB_STEP_SUMMARY
echo '```json' >> $GITHUB_STEP_SUMMARY
cat release/firmware-*.mt.json >> $GITHUB_STEP_SUMMARY
echo '' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
id: upload
with:
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}
overwrite: true
path: |
release/*.mt.json
release/*.bin
release/*.elf
release/*.uf2
release/*.hex
release/*-ota.zip
release/*.zip
release/device-*.sh
release/device-*.bat

View File

@@ -1,500 +0,0 @@
name: Build One Arch
on:
workflow_dispatch:
inputs:
arch:
type: choice
options:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
- native
jobs:
setup:
strategy:
fail-fast: false
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/setup-python@v6
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 ${{inputs.arch}} extra)
else
TARGETS=$(./bin/generate_ci_matrix.py ${{inputs.arch}} pr)
fi
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF Targets: $TARGETS"
echo "${{inputs.arch}}=$(jq -cn --argjson environments "$TARGETS" '{board: $environments}')" >> $GITHUB_OUTPUT
outputs:
esp32: ${{ steps.jsonStep.outputs.esp32 }}
esp32s3: ${{ steps.jsonStep.outputs.esp32s3 }}
esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }}
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 }}
build-esp32:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32
build-esp32s3:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32s3'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32s3
build-esp32c3:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32c3'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c3
build-esp32c6:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'esp32c6'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c6
build-nrf52840:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'nrf52840'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: nrf52840
build-rp2040:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'rp2040'}}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: rp2040
build-rp2350:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == '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:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'stm32' }}
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: stm32
build-debian-src:
if: ${{ github.repository == 'meshtastic/firmware' && github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/build_debian_src.yml
with:
series: UNRELEASED
build_location: local
secrets: inherit
package-pio-deps-native-tft:
if: ${{ inputs.arch == 'native' }}
uses: ./.github/workflows/package_pio_deps.yml
with:
pio_env: native-tft
secrets: inherit
test-native:
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' }}
uses: ./.github/workflows/test_native.yml
docker-deb-amd64:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-deb-amd64-tft:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-alp-amd64:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-alp-amd64-tft:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-deb-arm64:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm64
runs-on: ubuntu-24.04-arm
push: false
docker-deb-armv7:
if: ${{ github.event_name != 'workflow_dispatch' || inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm/v7
runs-on: ubuntu-24.04-arm
push: false
gather-artifacts:
permissions:
contents: write
pull-requests: write
strategy:
fail-fast: false
matrix:
arch:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
runs-on: ubuntu-latest
needs:
[
version,
build-esp32,
build-esp32s3,
build-esp32c3,
build-esp32c6,
build-nrf52840,
build-rp2040,
build-rp2350,
build-stm32,
]
steps:
- name: Checkout code
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@v5
with:
path: ./
pattern: firmware-${{inputs.arch}}-*
merge-multiple: true
- name: Display structure of downloaded files
run: ls -R
- 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-${{inputs.arch}}-${{ needs.version.outputs.long }}
overwrite: true
path: |
./firmware-*.bin
./firmware-*.uf2
./firmware-*.hex
./firmware-*-ota.zip
./device-*.sh
./device-*.bat
./littlefs-*.bin
./bleota*bin
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
- uses: actions/download-artifact@v5
with:
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output
# For diagnostics
- name: Show artifacts
run: ls -lR
- name: Device scripts permissions
run: |
chmod +x ./output/device-install.sh
chmod +x ./output/device-update.sh
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip
uses: actions/upload-artifact@v4
with:
name: debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
overwrite: true
path: ./*.elf
retention-days: 30
- uses: scruplelesswizard/comment-artifact@main
if: ${{ github.event_name == 'pull_request' }}
with:
name: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
description: "Download firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
github-token: ${{ secrets.GITHUB_TOKEN }}
release-artifacts:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
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@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- name: Create release
uses: softprops/action-gh-release@v2
id: create_release
with:
draft: true
prerelease: true
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@v5
with:
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@v5
with:
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-${{ 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
run: ls -lR
- name: Add Linux sources to GtiHub Release
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
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 }}
release-firmware:
strategy:
fail-fast: false
matrix:
arch:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
with:
pattern: firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output
- name: Display structure of downloaded files
run: ls -lR
- name: Device scripts permissions
run: |
chmod +x ./output/device-install.sh
chmod +x ./output/device-update.sh
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v5
with:
name: debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
merge-multiple: true
path: ./elfs
- name: Zip debug elfs
run: zip -j -9 -r ./debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip ./elfs
# For diagnostics
- name: Display structure of downloaded files
run: ls -lR
- name: Add bins and debug elfs to GitHub Release
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
gh release upload v${{ needs.version.outputs.long }} ./debug-elfs-${{inputs.arch}}-${{ needs.version.outputs.long }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish-firmware:
runs-on: ubuntu-24.04
if: ${{ github.event_name == 'workflow_dispatch' }}
needs: [release-firmware, version]
env:
targets: |-
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./publish
- name: Publish firmware to meshtastic.github.io
uses: peaceiris/actions-gh-pages@v4
env:
# On event/* branches, use the event name as the destination prefix
DEST_PREFIX: ${{ contains(github.ref_name, 'event/') && format('{0}/', github.ref_name) || '' }}
with:
deploy_key: ${{ secrets.DIST_PAGES_DEPLOY_KEY }}
external_repository: meshtastic/meshtastic.github.io
publish_branch: master
publish_dir: ./publish
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: ${{ needs.version.outputs.long }}
enable_jekyll: true

View File

@@ -3,6 +3,7 @@ name: Build One Target
on:
workflow_dispatch:
inputs:
# trunk-ignore(checkov/CKV_GHA_7)
arch:
type: choice
options:
@@ -14,16 +15,17 @@ on:
- rp2040
- rp2350
- stm32
- native
target:
type: string
required: false
description: Choose the target board, e.g. nrf52_promicro_diy_tcxo. If blank, will find available targets.
# find-target:
# type: boolean
# default: true
# description: 'Find the available targets'
permissions: read-all
jobs:
find-targets:
if: ${{ inputs.target == '' }}
@@ -39,10 +41,9 @@ jobs:
- rp2040
- rp2350
- stm32
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.x
@@ -51,19 +52,19 @@ jobs:
- name: Generate matrix
id: jsonStep
run: |
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} extra)
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} --level extra)
echo "Name: $GITHUB_REF_NAME" >> $GITHUB_STEP_SUMMARY
echo "Base: $GITHUB_BASE_REF" >> $GITHUB_STEP_SUMMARY
echo "Arch: ${{matrix.arch}}" >> $GITHUB_STEP_SUMMARY
echo "Ref: $GITHUB_REF" >> $GITHUB_STEP_SUMMARY
echo "Targets:" >> $GITHUB_STEP_SUMMARY
echo $TARGETS | sed 's/[][]//g; s/", "/\n- /g; s/"//g; s/^/- /' >> $GITHUB_STEP_SUMMARY
echo $TARGETS | jq -r 'sort_by(.board) |.[] | "- " + .board' >> $GITHUB_STEP_SUMMARY
version:
if: ${{ inputs.target != '' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -75,108 +76,29 @@ jobs:
long: ${{ steps.version.outputs.long }}
deb: ${{ steps.version.outputs.deb }}
build-arch:
build:
if: ${{ inputs.target != '' && inputs.arch != 'native' }}
needs: [version]
strategy:
fail-fast: false
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ inputs.target }}
platform: ${{ inputs.arch }}
build-debian-src:
if: ${{ github.repository == 'meshtastic/firmware' && inputs.arch == 'native' }}
uses: ./.github/workflows/build_debian_src.yml
with:
series: UNRELEASED
build_location: local
secrets: inherit
package-pio-deps-native-tft:
if: ${{ inputs.arch == 'native' }}
uses: ./.github/workflows/package_pio_deps.yml
with:
pio_env: native-tft
secrets: inherit
test-native:
if: ${{ !contains(github.ref_name, 'event/') && github.event_name != 'workflow_dispatch' || !contains(github.ref_name, 'event/') && inputs.arch == 'native' && inputs.target != '' }}
uses: ./.github/workflows/test_native.yml
docker-deb-amd64:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-deb-amd64-tft:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-alp-amd64:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-alp-amd64-tft:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-deb-arm64:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm64
runs-on: ubuntu-24.04-arm
push: false
docker-deb-armv7:
if: ${{ inputs.target != '' && inputs.arch == 'native' }}
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm/v7
runs-on: ubuntu-24.04-arm
push: false
gather-artifacts:
permissions:
contents: write
pull-requests: write
strategy:
fail-fast: false
runs-on: ubuntu-latest
needs: [version, build-arch]
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
path: ./
pattern: firmware-*-*
@@ -189,7 +111,7 @@ jobs:
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
overwrite: true
@@ -197,7 +119,7 @@ jobs:
./firmware-*.bin
./firmware-*.uf2
./firmware-*.hex
./firmware-*-ota.zip
./firmware-*.zip
./device-*.sh
./device-*.bat
./littlefs-*.bin
@@ -205,7 +127,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
pattern: firmware-*-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -217,14 +139,14 @@ jobs:
- name: Device scripts permissions
run: |
chmod +x ./output/device-install.sh
chmod +x ./output/device-update.sh
chmod +x ./output/device-install.sh || true
chmod +x ./output/device-update.sh || true
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
overwrite: true
@@ -237,159 +159,3 @@ jobs:
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
description: "Download firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip. This artifact will be available for 90 days from creation"
github-token: ${{ secrets.GITHUB_TOKEN }}
release-artifacts:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' && inputs.target != ''}}
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@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- name: Create release
uses: softprops/action-gh-release@v2
id: create_release
with:
draft: true
prerelease: true
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@v5
with:
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@v5
with:
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-${{ 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
run: ls -lR
- name: Add Linux sources to GtiHub Release
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
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 }}
release-firmware:
strategy:
fail-fast: false
runs-on: ubuntu-latest
if: ${{ github.event_name == 'workflow_dispatch' && inputs.target != ''}}
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
with:
pattern: firmware-*-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./output
- name: Display structure of downloaded files
run: ls -lR
- name: Device scripts permissions
run: |
chmod +x ./output/device-install.sh
chmod +x ./output/device-update.sh
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v5
with:
pattern: debug-elfs-*-${{ needs.version.outputs.long }}.zip
merge-multiple: true
path: ./elfs
- name: Zip debug elfs
run: zip -j -9 -r ./debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./elfs
# For diagnostics
- name: Display structure of downloaded files
run: ls -lR
- name: Add bins and debug elfs to GitHub Release
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
gh release upload v${{ needs.version.outputs.long }} ./debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
publish-firmware:
runs-on: ubuntu-24.04
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'meshtastic/firmware' && inputs.target != '' }}
needs: [release-firmware, version]
env:
targets: |-
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./publish
- name: Publish firmware to meshtastic.github.io
uses: peaceiris/actions-gh-pages@v4
env:
# On event/* branches, use the event name as the destination prefix
DEST_PREFIX: ${{ contains(github.ref_name, 'event/') && format('{0}/', github.ref_name) || '' }}
with:
deploy_key: ${{ secrets.DIST_PAGES_DEPLOY_KEY }}
external_repository: meshtastic/meshtastic.github.io
publish_branch: master
publish_dir: ./publish
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: ${{ needs.version.outputs.long }}
enable_jekyll: true

View File

@@ -47,7 +47,7 @@ jobs:
runs-on: ${{ inputs.runs-on }}
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
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@v5
uses: actions/checkout@v6
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@v5
uses: actions/checkout@v6
with:
submodules: recursive
ref: ${{ github.ref }}

View File

@@ -27,58 +27,39 @@ on:
jobs:
setup:
if: github.repository == 'meshtastic/firmware'
strategy:
fail-fast: false
fail-fast: true
matrix:
arch:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
- all
- check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.x
cache: pip
- run: pip install -U platformio
- name: Uncomment build epoch
shell: bash
run: |
sed -i 's/#-DBUILD_EPOCH=$UNIX_TIME/-DBUILD_EPOCH=$UNIX_TIME/' platformio.ini
- 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}} pr)
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} --level 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
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF"
echo "${{matrix.arch}}=$TARGETS" >> $GITHUB_OUTPUT
echo "$TARGETS" >> $GITHUB_STEP_SUMMARY
outputs:
esp32: ${{ steps.jsonStep.outputs.esp32 }}
esp32s3: ${{ steps.jsonStep.outputs.esp32s3 }}
esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }}
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 }}
all: ${{ steps.jsonStep.outputs.all }}
check: ${{ steps.jsonStep.outputs.check }}
version:
if: github.repository == 'meshtastic/firmware'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -94,105 +75,35 @@ jobs:
needs: setup
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.check) }}
runs-on: ubuntu-latest
matrix:
check: ${{ fromJson(needs.setup.outputs.check) }}
# Use 'arctastic' self-hosted runner pool when checking in the main repo
runs-on: ${{ github.repository_owner == 'meshtastic' && 'arctastic' || 'ubuntu-latest' }}
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
steps:
- uses: actions/checkout@v5
- name: Build base
id: base
uses: ./.github/actions/setup-base
- name: Check ${{ matrix.board }}
run: bin/check-all.sh ${{ matrix.board }}
- uses: actions/checkout@v6
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Check ${{ matrix.check.board }}
uses: meshtastic/gh-action-firmware@main
with:
pio_platform: ${{ matrix.check.platform }}
pio_env: ${{ matrix.check.board }}
pio_target: check
build-esp32:
build:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
matrix:
build: ${{ fromJson(needs.setup.outputs.all) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32
build-esp32s3:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32s3
build-esp32c3:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c3
build-esp32c6:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c6
build-nrf52840:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: nrf52840
build-rp2040:
needs: [setup, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
uses: ./.github/workflows/build_firmware.yml
with:
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, version]
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: stm32
pio_env: ${{ matrix.build.board }}
platform: ${{ matrix.build.platform }}
build-debian-src:
if: github.repository == 'meshtastic/firmware'
@@ -203,7 +114,7 @@ jobs:
secrets: inherit
package-pio-deps-native-tft:
if: ${{ github.event_name == 'workflow_dispatch' }}
if: ${{ github.repository == 'meshtastic/firmware' && github.event_name == 'workflow_dispatch' }}
uses: ./.github/workflows/package_pio_deps.yml
with:
pio_env: native-tft
@@ -213,60 +124,26 @@ jobs:
if: ${{ !contains(github.ref_name, 'event/') && github.repository == 'meshtastic/firmware' }}
uses: ./.github/workflows/test_native.yml
docker-deb-amd64:
if: github.repository == 'meshtastic/firmware'
docker:
strategy:
fail-fast: false
matrix:
distro: [debian, alpine]
platform: [linux/amd64, linux/arm64, linux/arm/v7]
pio_env: [native, native-tft]
exclude:
- distro: alpine
platform: linux/arm/v7
- pio_env: native-tft
platform: linux/arm64
- pio_env: native-tft
platform: linux/arm/v7
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-deb-amd64-tft:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-alp-amd64:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-alp-amd64-tft:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-deb-arm64:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm64
runs-on: ubuntu-24.04-arm
push: false
docker-deb-armv7:
if: github.repository == 'meshtastic/firmware'
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm/v7
runs-on: ubuntu-24.04-arm
distro: ${{ matrix.distro }}
platform: ${{ matrix.platform }}
runs-on: ${{ contains(matrix.platform, 'arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
pio_env: ${{ matrix.pio_env }}
push: false
gather-artifacts:
@@ -288,26 +165,15 @@ jobs:
- rp2350
- stm32
runs-on: ubuntu-latest
needs:
[
version,
build-esp32,
build-esp32s3,
build-esp32c3,
build-esp32c6,
build-nrf52840,
build-rp2040,
build-rp2350,
build-stm32,
]
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
path: ./
pattern: firmware-${{matrix.arch}}-*
@@ -316,19 +182,17 @@ jobs:
- name: Display structure of downloaded files
run: ls -R
- 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
uses: actions/upload-artifact@v5
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true
path: |
./firmware-*.mt.json
./firmware-*.bin
./firmware-*.uf2
./firmware-*.hex
./firmware-*-ota.zip
./firmware-*.zip
./device-*.sh
./device-*.bat
./littlefs-*.bin
@@ -336,7 +200,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -348,16 +212,16 @@ jobs:
- name: Device scripts permissions
run: |
chmod +x ./output/device-install.sh
chmod +x ./output/device-update.sh
chmod +x ./output/device-install.sh || true
chmod +x ./output/device-update.sh || true
- name: Zip firmware
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
uses: actions/upload-artifact@v5
with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true
path: ./*.elf
retention-days: 30
@@ -375,18 +239,14 @@ jobs:
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
needs:
- setup
- version
- gather-artifacts
- build-debian-src
- package-pio-deps-native-tft
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
uses: actions/checkout@v6
- name: Create release
uses: softprops/action-gh-release@v2
@@ -400,14 +260,14 @@ jobs:
Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
with:
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@v5
uses: actions/download-artifact@v6
with:
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -423,10 +283,25 @@ jobs:
- name: Display structure of downloaded files
run: ls -lR
- name: Add Linux sources to GtiHub Release
- name: Generate Release manifest
run: |
jq -n --arg ver "${{ needs.version.outputs.long }}" --argjson targets ${{ toJson(needs.setup.outputs.all) }} '{
"version": $ver,
"targets": $targets
}' > firmware-${{ needs.version.outputs.long }}.json
- name: Save Release manifest artifact
uses: actions/upload-artifact@v5
with:
name: manifest-${{ needs.version.outputs.long }}
overwrite: true
path: firmware-${{ needs.version.outputs.long }}.json
- name: Add sources to GitHub Release
# Only run when targeting master branch with workflow_dispatch
if: ${{ github.ref_name == 'master' }}
run: |
gh release upload v${{ needs.version.outputs.long }} ./firmware-${{ needs.version.outputs.long }}.json
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:
@@ -450,14 +325,14 @@ jobs:
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -468,15 +343,15 @@ jobs:
- name: Device scripts permissions
run: |
chmod +x ./output/device-install.sh
chmod +x ./output/device-update.sh
chmod +x ./output/device-install.sh || true
chmod +x ./output/device-update.sh || true
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./elfs
@@ -505,19 +380,26 @@ jobs:
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
- name: Get firmware artifacts
uses: actions/download-artifact@v6
with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./publish
- name: Get manifest artifact
uses: actions/download-artifact@v6
with:
pattern: manifest-${{ needs.version.outputs.long }}
path: ./publish
- name: Publish firmware to meshtastic.github.io
uses: peaceiris/actions-gh-pages@v4
env:

View File

@@ -7,27 +7,17 @@ on:
# Merge group is a special trigger that is used to trigger the workflow when a merge group is created.
merge_group:
env:
FAIL_FAST_PER_ARCH: true
jobs:
setup:
strategy:
fail-fast: true
matrix:
arch:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
- all
- check
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: 3.x
@@ -39,25 +29,18 @@ jobs:
if [[ "$GITHUB_HEAD_REF" == "" ]]; then
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}})
else
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} pr)
TARGETS=$(./bin/generate_ci_matrix.py ${{matrix.arch}} --level 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
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF"
echo "${{matrix.arch}}=$TARGETS" >> $GITHUB_OUTPUT
outputs:
esp32: ${{ steps.jsonStep.outputs.esp32 }}
esp32s3: ${{ steps.jsonStep.outputs.esp32s3 }}
esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }}
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 }}
all: ${{ steps.jsonStep.outputs.all }}
check: ${{ steps.jsonStep.outputs.check }}
version:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Get release version string
run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -73,105 +56,29 @@ jobs:
needs: setup
strategy:
fail-fast: true
matrix: ${{ fromJson(needs.setup.outputs.check) }}
matrix:
check: ${{ fromJson(needs.setup.outputs.check) }}
runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Build base
id: base
uses: ./.github/actions/setup-base
- name: Check ${{ matrix.board }}
run: bin/check-all.sh ${{ matrix.board }}
- name: Check ${{ matrix.check.board }}
run: bin/check-all.sh ${{ matrix.check.board }}
build-esp32:
build:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.esp32) }}
matrix:
build: ${{ fromJson(needs.setup.outputs.all) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32
build-esp32s3:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.esp32s3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32s3
build-esp32c3:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.esp32c3) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c3
build-esp32c6:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.esp32c6) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: esp32c6
build-nrf52840:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.nrf52840) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: nrf52840
build-rp2040:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.rp2040) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: rp2040
build-rp2350:
needs: [setup, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
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, version]
strategy:
fail-fast: ${{ vars.FAIL_FAST_PER_ARCH == true }}
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.board }}
platform: stm32
pio_env: ${{ matrix.build.board }}
platform: ${{ matrix.build.platform }}
build-debian-src:
if: github.repository == 'meshtastic/firmware'
@@ -192,54 +99,26 @@ jobs:
if: ${{ !contains(github.ref_name, 'event/') }}
uses: ./.github/workflows/test_native.yml
docker-deb-amd64:
docker:
strategy:
fail-fast: false
matrix:
distro: [debian, alpine]
platform: [linux/amd64, linux/arm64, linux/arm/v7]
pio_env: [native, native-tft]
exclude:
- distro: alpine
platform: linux/arm/v7
- pio_env: native-tft
platform: linux/arm64
- pio_env: native-tft
platform: linux/arm/v7
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-deb-amd64-tft:
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-alp-amd64:
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
docker-alp-amd64-tft:
uses: ./.github/workflows/docker_build.yml
with:
distro: alpine
platform: linux/amd64
runs-on: ubuntu-24.04
push: false
pio_env: native-tft
docker-deb-arm64:
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm64
runs-on: ubuntu-24.04-arm
push: false
docker-deb-armv7:
uses: ./.github/workflows/docker_build.yml
with:
distro: debian
platform: linux/arm/v7
runs-on: ubuntu-24.04-arm
distro: ${{ matrix.distro }}
platform: ${{ matrix.platform }}
runs-on: ${{ contains(matrix.platform, 'arm') && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }}
pio_env: ${{ matrix.pio_env }}
push: false
gather-artifacts:
@@ -260,26 +139,15 @@ jobs:
- rp2350
- stm32
runs-on: ubuntu-latest
needs:
[
version,
build-esp32,
build-esp32s3,
build-esp32c3,
build-esp32c6,
build-nrf52840,
build-rp2040,
build-rp2350,
build-stm32,
]
needs: [version, build]
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
path: ./
pattern: firmware-${{matrix.arch}}-*
@@ -292,7 +160,7 @@ jobs:
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true
@@ -300,7 +168,7 @@ jobs:
./firmware-*.bin
./firmware-*.uf2
./firmware-*.hex
./firmware-*-ota.zip
./firmware-*.zip
./device-*.sh
./device-*.bat
./littlefs-*.bin
@@ -308,7 +176,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -320,16 +188,16 @@ jobs:
- name: Device scripts permissions
run: |
chmod +x ./output/device-install.sh
chmod +x ./output/device-update.sh
chmod +x ./output/device-install.sh || true
chmod +x ./output/device-update.sh || true
- name: Zip firmware
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
uses: actions/upload-artifact@v5
with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true
path: ./*.elf
retention-days: 30
@@ -353,12 +221,7 @@ jobs:
- package-pio-deps-native-tft
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
uses: actions/checkout@v6
- name: Create release
uses: softprops/action-gh-release@v2
@@ -372,14 +235,14 @@ jobs:
Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
with:
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@v5
uses: actions/download-artifact@v6
with:
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -422,14 +285,14 @@ jobs:
needs: [release-artifacts, version]
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
@@ -440,15 +303,15 @@ jobs:
- name: Device scripts permissions
run: |
chmod +x ./output/device-install.sh
chmod +x ./output/device-update.sh
chmod +x ./output/device-install.sh || true
chmod +x ./output/device-update.sh || true
- name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true
path: ./elfs
@@ -477,14 +340,14 @@ jobs:
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: 3.x
- uses: actions/download-artifact@v5
- uses: actions/download-artifact@v6
with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true

View File

@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Trunk Check
uses: trunk-io/trunk-action@v1
@@ -31,7 +31,7 @@ jobs:
pull-requests: write # For trunk to create PRs
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- 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@v5
uses: actions/checkout@v6
with:
submodules: recursive
path: meshtasticd
@@ -58,7 +58,7 @@ jobs:
id: version
- name: Download artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
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@v5
uses: actions/checkout@v6
with:
submodules: recursive
ref: ${{github.event.pull_request.head.ref}}
@@ -56,7 +56,7 @@ jobs:
PLATFORMIO_CORE_DIR: pio/core
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }}
overwrite: true

View File

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

View File

@@ -17,7 +17,7 @@ jobs:
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 requiredLabels = ['bugfix', 'enhancement', 'hardware-support', 'dependencies', 'submodules', 'github_actions', 'trunk', 'cleanup'];
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(', ')}.`);

View File

@@ -40,7 +40,7 @@ jobs:
checks: write
pull-requests: write
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
submodules: recursive
@@ -50,9 +50,9 @@ jobs:
- name: Download test artifacts
if: needs.native-tests.result != 'skipped'
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
with:
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
name: platformio-test-report-${{ steps.version.outputs.long }}
merge-multiple: true
- name: Parse test results and create detailed summary

View File

@@ -60,7 +60,10 @@ jobs:
shell: bash
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
# Always use master branch for version bumps
ref: master
- name: Setup Python
uses: actions/setup-python@v6
@@ -99,7 +102,7 @@ jobs:
PIP_DISABLE_PIP_VERSION_CHECK: 1
- name: Create Bumps pull request
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v8
with:
base: ${{ github.event.repository.default_branch }}
branch: create-pull-request/bump-version

View File

@@ -21,7 +21,7 @@ jobs:
steps:
# step 1
- name: clone application source code
uses: actions/checkout@v5
uses: actions/checkout@v6
# step 2
- name: full scan
@@ -33,7 +33,7 @@ jobs:
# step 3
- name: save report as pipeline artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: report.sarif
overwrite: true
@@ -41,7 +41,7 @@ jobs:
# step 4
- name: publish code scanning alerts
uses: github/codeql-action/upload-sarif@v3
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: report.sarif
category: semgrep

View File

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

View File

@@ -17,8 +17,10 @@ jobs:
steps:
- name: Stale PR+Issues
uses: actions/stale@v10.1.0
uses: actions/stale@v10.1.1
with:
days-before-stale: 45
exempt-issue-labels: pinned,3.0
exempt-pr-labels: pinned,3.0
stale-issue-message: This issue has not had any comment or update in the last month. If it is still relevant, please post update comments. If no comments are made, this issue will be closed automagically in 7 days.
close-issue-message: This issue has not had any comment since the last notice. It has been closed automatically. If this is incorrect, or the issue becomes relevant again, please request that it is reopened.
exempt-issue-labels: pinned,3.0,triaged,backlog
exempt-pr-labels: pinned,3.0,triaged,backlog

View File

@@ -14,7 +14,7 @@ jobs:
name: Native Simulator Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -40,7 +40,7 @@ jobs:
- name: Integration test
run: |
.pio/build/coverage/program &
.pio/build/coverage/meshtasticd -s &
PID=$!
timeout 20 bash -c "until ls -al /proc/$PID/fd | grep socket; do sleep 1; done"
echo "Simulator started, launching python test..."
@@ -59,10 +59,10 @@ jobs:
id: version
- name: Save coverage information
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
if: always() # run this step even if previous step failed
with:
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}.zip
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}
overwrite: true
path: ./coverage_*.info
@@ -70,7 +70,7 @@ jobs:
name: Native PlatformIO Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -94,9 +94,9 @@ jobs:
- name: Save test results
if: always() # run this step even if previous step failed
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
name: platformio-test-report-${{ steps.version.outputs.long }}
overwrite: true
path: ./testreport.xml
@@ -108,10 +108,10 @@ jobs:
sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative.
- name: Save coverage information
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
if: always() # run this step even if previous step failed
with:
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}.zip
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}
overwrite: true
path: ./coverage_*.info
@@ -127,7 +127,7 @@ jobs:
- platformio-tests
if: always()
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -137,22 +137,22 @@ jobs:
id: version
- name: Download test artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
with:
name: platformio-test-report-${{ steps.version.outputs.long }}.zip
name: platformio-test-report-${{ steps.version.outputs.long }}
merge-multiple: true
- name: Test Report
uses: dorny/test-reporter@v2.1.1
uses: dorny/test-reporter@v2.3.0
with:
name: PlatformIO Tests
path: testreport.xml
reporter: java-junit
- name: Download coverage artifacts
uses: actions/download-artifact@v5
uses: actions/download-artifact@v6
with:
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}.zip
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}
path: code-coverage-report
merge-multiple: true
@@ -163,7 +163,7 @@ jobs:
genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report
- name: Save Code Coverage Report
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: code-coverage-report-${{ steps.version.outputs.long }}.zip
name: code-coverage-report-${{ steps.version.outputs.long }}
path: code-coverage-report

View File

@@ -20,9 +20,9 @@ jobs:
runs-on: test-runner
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
# - uses: actions/setup-python@v5
# - uses: actions/setup-python@v6
# with:
# python-version: '3.10'
@@ -47,9 +47,9 @@ jobs:
pio upgrade
- name: Setup Node
uses: actions/setup-node@v5
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
- name: Setup pnpm
uses: pnpm/action-setup@v4

View File

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

View File

@@ -16,7 +16,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- 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@v5
uses: actions/checkout@v6
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@v5
uses: actions/checkout@v6
with:
submodules: true
@@ -31,7 +31,7 @@ jobs:
./bin/regen-protos.sh
- name: Create pull request
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v8
with:
branch: create-pull-request/update-protobufs
labels: submodules

View File

@@ -4,31 +4,31 @@ cli:
plugins:
sources:
- id: trunk
ref: v1.7.2
ref: v1.7.4
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- checkov@3.2.473
- renovate@41.132.5
- prettier@3.6.2
- trufflehog@3.90.8
- checkov@3.2.495
- renovate@42.48.0
- prettier@3.7.4
- trufflehog@3.92.3
- yamllint@1.37.1
- bandit@1.8.6
- trivy@0.67.0
- bandit@1.9.2
- trivy@0.68.1
- taplo@0.10.0
- ruff@0.13.2
- isort@6.1.0
- markdownlint@0.45.0
- oxipng@9.1.5
- ruff@0.14.9
- isort@7.0.0
- markdownlint@0.47.0
- oxipng@10.0.0
- svgo@4.0.0
- actionlint@1.7.7
- actionlint@1.7.9
- flake8@7.3.0
- hadolint@2.14.0
- shfmt@3.6.0
- shellcheck@0.11.0
- black@25.9.0
- black@25.12.0
- git-diff-check
- gitleaks@8.28.0
- gitleaks@8.30.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-slim-trixie AS builder
FROM python:3.14-slim-trixie AS builder
ARG PIO_ENV=native
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Etc/UTC

View File

@@ -37,4 +37,3 @@ Join our community and help improve Meshtastic! 🚀
## Stats
![Alt](https://repobeats.axiom.co/api/embed/8025e56c482ec63541593cc5bd322c19d5c0bdcf.svg "Repobeats analytics image")

View File

@@ -3,12 +3,13 @@
# trunk-ignore-all(hadolint/DL3018): Do not pin apk package versions
# trunk-ignore-all(hadolint/DL3013): Do not pin pip package versions
FROM python:3.13-alpine3.22 AS builder
FROM python:3.14-alpine3.22 AS builder
ARG PIO_ENV=native
ENV PIP_ROOT_USER_ACTION=ignore
RUN apk --no-cache add \
bash g++ libstdc++-dev linux-headers zip git ca-certificates libgpiod-dev yaml-cpp-dev bluez-dev \
bash g++ libstdc++-dev linux-headers zip git ca-certificates libbsd-dev \
libgpiod-dev yaml-cpp-dev bluez-dev \
libusb-dev i2c-tools-dev libuv-dev openssl-dev pkgconf argp-standalone \
libx11-dev libinput-dev libxkbcommon-dev \
&& rm -rf /var/cache/apk/* \
@@ -27,7 +28,7 @@ RUN bash ./bin/build-native.sh "$PIO_ENV" && \
# ##### PRODUCTION BUILD #############
FROM alpine:3.22
FROM alpine:3.23
LABEL org.opencontainers.image.title="Meshtastic" \
org.opencontainers.image.description="Alpine Meshtastic daemon" \
org.opencontainers.image.url="https://meshtastic.org" \
@@ -40,8 +41,8 @@ LABEL org.opencontainers.image.title="Meshtastic" \
USER root
RUN apk --no-cache add \
shadow libstdc++ libgpiod yaml-cpp libusb i2c-tools libuv \
libx11 libinput libxkbcommon \
shadow libstdc++ libbsd libgpiod yaml-cpp libusb \
i2c-tools libuv libx11 libinput libxkbcommon \
&& rm -rf /var/cache/apk/* \
&& mkdir -p /var/lib/meshtasticd \
&& mkdir -p /etc/meshtasticd/config.d \

165
bin/analyze_map.py Normal file
View File

@@ -0,0 +1,165 @@
#!/usr/bin/env python3
"""Summarise linker map output to highlight heavy object files and libraries.
Usage:
python bin/analyze_map.py --map .pio/build/rak4631/output.map --top 20
The script parses GNU ld map files and aggregates section sizes per object file
and per archive/library, then prints sortable tables that make it easy to spot
modules worth trimming or hiding behind feature flags.
"""
from __future__ import annotations
import argparse
import collections
import os
import re
import sys
from typing import DefaultDict, Dict, Tuple
SECTION_LINE_RE = re.compile(r"^\s+(?P<section>\S+)\s+0x[0-9A-Fa-f]+\s+0x(?P<size>[0-9A-Fa-f]+)\s+(?P<object>.+)$")
ARCHIVE_MEMBER_RE = re.compile(r"^(?P<archive>.+)\((?P<object>[^)]+)\)$")
def human_size(num_bytes: int) -> str:
"""Return a friendly size string with one decimal place."""
if num_bytes < 1024:
return f"{num_bytes:,} B"
num = float(num_bytes)
for unit in ("KB", "MB", "GB"):
num /= 1024.0
if num < 1024.0:
return f"{num:.1f} {unit}"
return f"{num:.1f} TB"
def shorten_path(path: str, root: str) -> str:
"""Prefer repository-relative paths for readability."""
path = path.strip()
if not path:
return path
# Normalise Windows archives (backslashes) to POSIX style for consistency.
path = path.replace("\\", "/")
# Attempt to strip the root when an absolute path lives inside the repo.
if os.path.isabs(path):
try:
rel = os.path.relpath(path, root)
if not rel.startswith(".."):
return rel
except ValueError:
# relpath can fail on mixed drives on Windows; fall back to basename.
pass
return path
def describe_object(raw_object: str, root: str) -> Tuple[str, str]:
"""Return a human friendly object label and the library it belongs to."""
raw_object = raw_object.strip()
lib_label = "[app]"
match = ARCHIVE_MEMBER_RE.match(raw_object)
if match:
archive = shorten_path(match.group("archive"), root)
obj = match.group("object")
lib_label = os.path.basename(archive) or archive
label = f"{archive}:{obj}"
else:
label = shorten_path(raw_object, root)
# If the object lives under libs, hint at the containing directory.
parent = os.path.basename(os.path.dirname(label))
if parent:
lib_label = parent
return label, lib_label
def parse_map(map_path: str, repo_root: str) -> Tuple[Dict[str, int], Dict[str, int], Dict[str, Dict[str, int]]]:
per_object: DefaultDict[str, int] = collections.defaultdict(int)
per_library: DefaultDict[str, int] = collections.defaultdict(int)
per_object_sections: DefaultDict[str, DefaultDict[str, int]] = collections.defaultdict(lambda: collections.defaultdict(int))
try:
with open(map_path, "r", encoding="utf-8", errors="ignore") as handle:
for line in handle:
match = SECTION_LINE_RE.match(line)
if not match:
continue
section = match.group("section")
if section.startswith("*") or section in {"LOAD", "ORIGIN"}:
continue
size = int(match.group("size"), 16)
if size == 0:
continue
obj_token = match.group("object").strip()
if not obj_token or obj_token.startswith("*") or "load address" in obj_token:
continue
label, lib_label = describe_object(obj_token, repo_root)
per_object[label] += size
per_library[lib_label] += size
per_object_sections[label][section] += size
except FileNotFoundError:
raise SystemExit(f"error: map file '{map_path}' not found. Run a build first.")
return per_object, per_library, per_object_sections
def format_section_breakdown(section_sizes: Dict[str, int], total: int, limit: int = 3) -> str:
items = sorted(section_sizes.items(), key=lambda kv: kv[1], reverse=True)
parts = []
for section, size in items[:limit]:
pct = (size / total) * 100 if total else 0
parts.append(f"{section} {pct:.1f}%")
if len(items) > limit:
remainder = total - sum(size for _, size in items[:limit])
pct = (remainder / total) * 100 if total else 0
parts.append(f"other {pct:.1f}%")
return ", ".join(parts)
def print_report(map_path: str, top_n: int, per_object: Dict[str, int], per_library: Dict[str, int], per_object_sections: Dict[str, Dict[str, int]]):
total_bytes = sum(per_object.values())
if total_bytes == 0:
print("No section data found in map file.")
return
print(f"Map file: {map_path}")
print(f"Accounted size: {human_size(total_bytes)} across {len(per_object)} object files\n")
sorted_objects = sorted(per_object.items(), key=lambda kv: kv[1], reverse=True)
print(f"Top {min(top_n, len(sorted_objects))} object files by linked size:")
for idx, (obj, size) in enumerate(sorted_objects[:top_n], 1):
pct = (size / total_bytes) * 100
breakdown = format_section_breakdown(per_object_sections[obj], size)
print(f"{idx:2}. {human_size(size):>9} ({size:,} B, {pct:5.2f}% of linked size)")
print(f" {obj}")
if breakdown:
print(f" sections: {breakdown}")
print()
sorted_libs = sorted(per_library.items(), key=lambda kv: kv[1], reverse=True)
print(f"Top {min(top_n, len(sorted_libs))} libraries or source roots:")
for idx, (lib, size) in enumerate(sorted_libs[:top_n], 1):
pct = (size / total_bytes) * 100
print(f"{idx:2}. {human_size(size):>9} ({size:,} B, {pct:5.2f}% of linked size) {lib}")
def main() -> None:
parser = argparse.ArgumentParser(description="Highlight heavy object files from a GNU ld map file.")
parser.add_argument("--map", default=".pio/build/rak4631/output.map", help="Path to the map file (default: %(default)s)")
parser.add_argument("--top", type=int, default=20, help="Number of entries to display per table (default: %(default)s)")
args = parser.parse_args()
map_path = os.path.abspath(args.map)
repo_root = os.path.abspath(os.getcwd())
per_object, per_library, per_object_sections = parse_map(map_path, repo_root)
print_report(os.path.relpath(map_path, repo_root), args.top, per_object, per_library, per_object_sections)
if __name__ == "__main__":
main()

View File

@@ -5,7 +5,8 @@ set -e
VERSION=`bin/buildinfo.py long`
SHORT_VERSION=`bin/buildinfo.py short`
OUTDIR=release/
BUILDDIR=.pio/build/$1
OUTDIR=release
rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
@@ -14,33 +15,27 @@ rm -r $OUTDIR/* || true
platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*
rm -f $BUILDDIR/firmware*
# The shell vars the build tool expects to find
export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION
pio run --environment $1 # -v
SRCELF=.pio/build/$1/firmware.elf
cp $SRCELF $OUTDIR/$basename.elf
pio run --environment $1 -t mtjson # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
echo "Copying ESP32 bin file"
SRCBIN=.pio/build/$1/firmware.factory.bin
cp $SRCBIN $OUTDIR/$basename.bin
cp $BUILDDIR/$basename.factory.bin $OUTDIR/$basename.factory.bin
echo "Copying ESP32 update bin file"
SRCBIN=.pio/build/$1/firmware.bin
cp $SRCBIN $OUTDIR/$basename-update.bin
cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin
echo "Building Filesystem for ESP32 targets"
# 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
cp bin/device-update.* $OUTDIR
echo "Copying Filesystem for ESP32 targets"
cp $BUILDDIR/littlefs-$1-$VERSION.bin $OUTDIR/littlefs-$1-$VERSION.bin
cp bin/device-install.* $OUTDIR/
cp bin/device-update.* $OUTDIR/
echo "Copying manifest"
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -17,15 +17,19 @@ VERSION=$(bin/buildinfo.py long)
SHORT_VERSION=$(bin/buildinfo.py short)
PIO_ENV=${1:-native}
OUTDIR=release/
BUILDDIR=.pio/build/$PIO_ENV
OUTDIR=release
rm -f $OUTDIR/firmware*
rm -f $OUTDIR/meshtasticd*
mkdir -p $OUTDIR/
rm -r $OUTDIR/* || true
basename=meshtasticd-$1-$VERSION
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
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
cp "$BUILDDIR/meshtasticd" "$OUTDIR/meshtasticd_linux_$(uname -m)"
cp bin/native-install.* $OUTDIR/

View File

@@ -5,7 +5,8 @@ set -e
VERSION=$(bin/buildinfo.py long)
SHORT_VERSION=$(bin/buildinfo.py short)
OUTDIR=release/
BUILDDIR=.pio/build/$1
OUTDIR=release
rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
@@ -14,40 +15,37 @@ rm -r $OUTDIR/* || true
platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*
rm -f $BUILDDIR/firmware*
# The shell vars the build tool expects to find
export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION
pio run --environment $1 # -v
SRCELF=.pio/build/$1/firmware.elf
cp $SRCELF $OUTDIR/$basename.elf
pio run --environment $1 -t mtjson # -v
echo "Generating NRF52 dfu file"
DFUPKG=.pio/build/$1/firmware.zip
cp $DFUPKG $OUTDIR/$basename-ota.zip
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
echo "Generating NRF52 uf2 file"
SRCHEX=.pio/build/$1/firmware.hex
echo "Copying NRF52 dfu (OTA) file"
cp $BUILDDIR/$basename.zip $OUTDIR/$basename.zip
# if WM1110 target, merge hex with softdevice 7.3.0
echo "Copying NRF52 UF2 file"
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2
cp bin/*.uf2 $OUTDIR/
SRCHEX=$BUILDDIR/$basename.hex
# if WM1110 target, copy the merged.hex
if (echo $1 | grep -q "wio-sdk-wm1110"); then
echo "Merging with softdevice"
bin/mergehex -m bin/s140_nrf52_7.3.0_softdevice.hex $SRCHEX -o .pio/build/$1/$basename.hex
SRCHEX=.pio/build/$1/$basename.hex
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/$basename.uf2 -f 0xADA52840
cp $SRCHEX $OUTDIR
cp bin/*.uf2 $OUTDIR
else
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/$basename.uf2 -f 0xADA52840
cp bin/device-install.* $OUTDIR
cp bin/device-update.* $OUTDIR
cp bin/*.uf2 $OUTDIR
echo "Copying .merged.hex file"
SRCHEX=$BUILDDIR/$basename.merged.hex
cp $SRCHEX $OUTDIR/
fi
if (echo $1 | grep -q "rak4631"); then
echo "Copying hex file"
cp .pio/build/$1/firmware.hex $OUTDIR/$basename.hex
fi
echo "Copying .hex file"
cp $SRCHEX $OUTDIR/
fi
echo "Copying manifest"
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -5,7 +5,8 @@ set -e
VERSION=`bin/buildinfo.py long`
SHORT_VERSION=`bin/buildinfo.py short`
OUTDIR=release/
BUILDDIR=.pio/build/$1
OUTDIR=release
rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
@@ -14,20 +15,19 @@ rm -r $OUTDIR/* || true
platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*
rm -f $BUILDDIR/firmware*
# The shell vars the build tool expects to find
export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION
pio run --environment $1 # -v
SRCELF=.pio/build/$1/firmware.elf
cp $SRCELF $OUTDIR/$basename.elf
pio run --environment $1 -t mtjson # -v
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
echo "Copying uf2 file"
SRCBIN=.pio/build/$1/firmware.uf2
cp $SRCBIN $OUTDIR/$basename.uf2
cp $BUILDDIR/$basename.uf2 $OUTDIR/$basename.uf2
cp bin/device-install.* $OUTDIR
cp bin/device-update.* $OUTDIR
echo "Copying manifest"
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -5,7 +5,8 @@ set -e
VERSION=$(bin/buildinfo.py long)
SHORT_VERSION=$(bin/buildinfo.py short)
OUTDIR=release/
BUILDDIR=.pio/build/$1
OUTDIR=release
rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
@@ -14,16 +15,19 @@ rm -r $OUTDIR/* || true
platformio pkg install -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*
rm -f $BUILDDIR/firmware*
# The shell vars the build tool expects to find
export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION
pio run --environment $1 # -v
SRCELF=.pio/build/$1/firmware.elf
cp $SRCELF $OUTDIR/$basename.elf
pio run --environment $1 -t mtjson # -v
SRCBIN=.pio/build/$1/firmware.bin
cp $SRCBIN $OUTDIR/$basename.bin
cp $BUILDDIR/$basename.elf $OUTDIR/$basename.elf
echo "Copying STM32 bin file"
cp $BUILDDIR/$basename.bin $OUTDIR/$basename.bin
echo "Copying manifest"
cp $BUILDDIR/$basename.mt.json $OUTDIR/$basename.mt.json

View File

@@ -184,6 +184,8 @@ Input:
Logging:
LogLevel: info # debug, info, warn, error
# TraceFile: /var/log/meshtasticd.json
# JSONFile: /packets.json # File location for JSON output of decoded packets
# JSONFilter: position # filter for packets to save to JSON file
# AsciiLogs: true # default if not specified is !isatty() on stdout
Webserver:

View File

@@ -5,22 +5,14 @@ TITLE Meshtastic device-install
SET "SCRIPT_NAME=%~nx0"
SET "DEBUG=0"
SET "PYTHON="
SET "TFT_BUILD=0"
SET "BIGDB8=0"
SET "MUIDB8=0"
SET "BIGDB16=0"
SET "ESPTOOL_BAUD=115200"
SET "ESPTOOL_CMD="
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 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=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 "MUIDB_8MB=picomputer-s3 unphone seeed-sensecap-indicator"
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"
@REM Default offsets.
@REM https://github.com/meshtastic/web-flasher/blob/main/stores/firmwareStore.ts#L202
SET "OTA_OFFSET=0x260000"
SET "SPIFFS_OFFSET=0x300000"
GOTO getopts
:help
@@ -29,7 +21,7 @@ ECHO.
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)
ECHO -f filename The firmware .factory.bin file to flash. Custom to your device type and region. (required)
ECHO The file must be located in this current directory.
ECHO -p PORT Set the environment variable for ESPTOOL_PORT.
ECHO If not set, ESPTOOL iterates all ports (Dangerous).
@@ -40,12 +32,12 @@ ECHO --1200bps-reset Attempt to place the device in correct mode. (1200bps
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
ECHO Example: %SCRIPT_NAME% -f firmware-t-deck-tft-2.6.0.0b106d4.factory.bin -p COM11
ECHO Example: %SCRIPT_NAME% -f firmware-unphone-2.6.0.0b106d4.factory.bin -p COM11
GOTO eof
:version
ECHO %SCRIPT_NAME% [Version 2.6.2]
ECHO %SCRIPT_NAME% [Version 2.7.0]
ECHO Meshtastic
GOTO eof
@@ -78,8 +70,8 @@ IF "__!FILENAME!__"=="____" (
CALL :LOG_MESSAGE ERROR "Filename containing spaces are not supported."
GOTO help
)
IF "__!FILENAME:firmware-=!__"=="__!FILENAME!__" (
CALL :LOG_MESSAGE ERROR "Filename must be a firmware-* file."
IF NOT "__!FILENAME:.factory.bin=!__"=="__!FILENAME!__" (
CALL :LOG_MESSAGE ERROR "Filename must be a firmware-*.factory.bin file."
GOTO help
)
@REM Remove ".\" or "./" file prefix if present.
@@ -93,12 +85,26 @@ IF NOT EXIST !FILENAME! (
GOTO eof
)
IF NOT "!FILENAME:update=!"=="!FILENAME!" (
CALL :LOG_MESSAGE DEBUG "We are working with a *update* file. !FILENAME!"
CALL :LOG_MESSAGE INFO "Use script device-update.bat to flash update !FILENAME!."
GOTO eof
CALL :LOG_MESSAGE DEBUG "Checking for metadata..."
@REM Derive metadata filename from firmware filename.
SET "METAFILE=!FILENAME:.factory.bin=!.mt.json"
IF EXIST !METAFILE! (
@REM Print parsed json with powershell
CALL :LOG_MESSAGE INFO "Firmware metadata: !METAFILE!"
powershell -NoProfile -Command "(Get-Content '!METAFILE!' | ConvertFrom-Json | Out-String).Trim()"
@REM Save metadata values to variables for later use.
FOR /f "usebackq" %%A IN (`powershell -NoProfile -Command ^
"(Get-Content '!METAFILE!' | ConvertFrom-Json).mcu"`) DO SET "MCU=%%A"
FOR /f "usebackq" %%A IN (`powershell -NoProfile -Command ^
"(Get-Content '!METAFILE!' | ConvertFrom-Json).part | Where-Object { $_.subtype -eq 'ota_1' } | Select-Object -ExpandProperty offset"`
) DO SET "OTA_OFFSET=%%A"
FOR /f "usebackq" %%A IN (`powershell -NoProfile -Command ^
"(Get-Content '!METAFILE!' | ConvertFrom-Json).part | Where-Object { $_.subtype -eq 'spiffs' } | Select-Object -ExpandProperty offset"`
) DO SET "SPIFFS_OFFSET=%%A"
) ELSE (
CALL :LOG_MESSAGE DEBUG "We are NOT working with a *update* file. !FILENAME!"
CALL :LOG_MESSAGE ERROR "No metadata file found: !METAFILE!"
GOTO eof
)
:skip-filename
@@ -108,7 +114,7 @@ IF NOT "__%PYTHON%__"=="____" (
SET "ESPTOOL_CMD=!PYTHON! -m esptool"
CALL :LOG_MESSAGE DEBUG "Python interpreter supplied."
) ELSE (
CALL :LOG_MESSAGE DEBUG "Python interpreter NOT supplied. Looking for esptool...
CALL :LOG_MESSAGE DEBUG "Python interpreter NOT supplied. Looking for esptool..."
WHERE esptool >nul 2>&1
IF %ERRORLEVEL% EQU 0 (
@REM WHERE exits with code 0 if esptool is found.
@@ -146,100 +152,26 @@ IF %BPS_RESET% EQU 1 (
GOTO eof
)
@REM Check if FILENAME contains "-tft-" and set target partitionScheme accordingly.
@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!"
SET "TFT_BUILD=1"
@REM Extract PROGNAME from %FILENAME% for later use.
SET "PROGNAME=!FILENAME:.factory.bin=!"
CALL :LOG_MESSAGE DEBUG "Computed PROGNAME: !PROGNAME!"
IF "__!MCU!__" == "__esp32s3__" (
@REM We are working with ESP32-S3
SET "OTA_FILENAME=bleota-s3.bin"
) ELSE IF "__!MCU!__" == "__esp32c3__" (
@REM We are working with ESP32-C3
SET "OTA_FILENAME=bleota-c3.bin"
) ELSE (
CALL :LOG_MESSAGE DEBUG "We are NOT working with a *-tft-* file. !FILENAME!"
@REM Everything else
SET "OTA_FILENAME=bleota.bin"
)
FOR %%a IN (%BIGDB_8MB%) DO (
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
@REM We are working with any of %BIGDB_8MB%.
SET "BIGDB8=1"
GOTO end_loop_bigdb_8mb
)
)
:end_loop_bigdb_8mb
FOR %%a IN (%MUIDB_8MB%) DO (
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
@REM We are working with any of %MUIDB_8MB%.
SET "MUIDB8=1"
GOTO end_loop_muidb_8mb
)
)
:end_loop_muidb_8mb
FOR %%a IN (%BIGDB_16MB%) DO (
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
@REM We are working with any of %BIGDB_16MB%.
SET "BIGDB16=1"
GOTO end_loop_bigdb_16mb
)
)
:end_loop_bigdb_16mb
IF %BIGDB8% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 8mb partition selected."
IF %MUIDB8% EQU 1 CALL :LOG_MESSAGE INFO "MUIDB 8mb partition selected."
IF %BIGDB16% EQU 1 CALL :LOG_MESSAGE INFO "BigDB 16mb partition selected."
@REM Extract BASENAME from %FILENAME% for later use.
SET "BASENAME=!FILENAME:firmware-=!"
CALL :LOG_MESSAGE DEBUG "Computed firmware basename: !BASENAME!"
@REM Account for S3 and C3 board's different OTA partition.
FOR %%a IN (%S3%) DO (
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
@REM We are working with any of %S3%.
SET "OTA_FILENAME=bleota-s3.bin"
GOTO :end_loop_s3
)
)
FOR %%a IN (%C3%) DO (
IF NOT "!FILENAME:%%a=!"=="!FILENAME!" (
@REM We are working with any of %C3%.
SET "OTA_FILENAME=bleota-c3.bin"
GOTO :end_loop_c3
)
)
@REM Everything else
SET "OTA_FILENAME=bleota.bin"
:end_loop_s3
:end_loop_c3
CALL :LOG_MESSAGE DEBUG "Set OTA_FILENAME to: !OTA_FILENAME!"
@REM Set SPIFFS filename with "littlefs-" prefix.
SET "SPIFFS_FILENAME=littlefs-%BASENAME%"
SET "SPIFFS_FILENAME=littlefs-!PROGNAME:firmware-=!.bin"
CALL :LOG_MESSAGE DEBUG "Set SPIFFS_FILENAME to: !SPIFFS_FILENAME!"
@REM Default offsets.
@REM https://github.com/meshtastic/web-flasher/blob/main/stores/firmwareStore.ts#L202
SET "OTA_OFFSET=0x260000"
SET "SPIFFS_OFFSET=0x300000"
@REM Offsets for BigDB 8mb.
IF %BIGDB8% EQU 1 (
SET "OTA_OFFSET=0x340000"
SET "SPIFFS_OFFSET=0x670000"
)
@REM Offsets for MUIDB 8mb.
IF %MUIDB8% EQU 1 (
SET "OTA_OFFSET=0x5D0000"
SET "SPIFFS_OFFSET=0x670000"
)
@REM Offsets for BigDB 16mb.
IF %BIGDB16% EQU 1 (
SET "OTA_OFFSET=0x650000"
SET "SPIFFS_OFFSET=0xc90000"
)
CALL :LOG_MESSAGE DEBUG "Set OTA_OFFSET to: !OTA_OFFSET!"
CALL :LOG_MESSAGE DEBUG "Set SPIFFS_OFFSET to: !SPIFFS_OFFSET!"

View File

@@ -2,67 +2,15 @@
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
BPS_RESET=false
TFT_BUILD=false
MCU=""
# Constants
RESET_BAUD=1200
FIRMWARE_OFFSET=0x00
# Variant groups
BIGDB_8MB=(
"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"
)
MUIDB_8MB=(
"picomputer-s3"
"unphone"
"seeed-sensecap-indicator"
)
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"
)
S3_VARIANTS=(
"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"
)
# Default littlefs* offset.
OFFSET=0x300000
# Default OTA Offset
OTA_OFFSET=0x260000
# Determine the correct esptool command to use
if "$PYTHON" -m esptool version >/dev/null 2>&1; then
@@ -76,6 +24,14 @@ else
exit 1
fi
# Check for jq
if ! command -v jq >/dev/null 2>&1; then
echo "Error: jq not found" >&2
echo "Install jq with your package manager." >&2
echo "e.g. 'apt install jq', 'dnf install jq', 'brew install jq', etc." >&2
exit 1
fi
set -e
# Usage info
@@ -87,7 +43,7 @@ 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.
-f FILENAME The firmware *.factory.bin file to flash. Custom to your device type and region.
--1200bps-reset Attempt to place the device in correct mode. Some hardware requires this twice. (1200bps Reset)
EOF
@@ -136,69 +92,43 @@ fi
shift
}
if [[ "$FILENAME" != firmware-* ]]; then
echo "Filename must be a firmware-* file."
if [[ $(basename "$FILENAME") != firmware-*.factory.bin ]]; then
echo "Filename must be a firmware-*.factory.bin file."
exit 1
fi
# Check if FILENAME contains "-tft-" and set target partitionScheme accordingly.
if [[ "${FILENAME//-tft-/}" != "$FILENAME" ]]; then
TFT_BUILD=true
fi
# Extract PROGNAME from %FILENAME% for later use.
PROGNAME="${FILENAME/.factory.bin/}"
# Derive metadata filename from %PROGNAME%.
METAFILE="${PROGNAME}.mt.json"
# Extract BASENAME from %FILENAME% for later use.
BASENAME="${FILENAME/firmware-/}"
if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
# Default littlefs* offset.
OFFSET=0x300000
# Default OTA Offset
OTA_OFFSET=0x260000
# littlefs* offset for BigDB 8mb and OTA OFFSET.
for variant in "${BIGDB_8MB[@]}"; do
if [ -z "${FILENAME##*"$variant"*}" ]; then
OFFSET=0x670000
OTA_OFFSET=0x340000
fi
done
for variant in "${MUIDB_8MB[@]}"; do
if [ -z "${FILENAME##*"$variant"*}" ]; then
OFFSET=0x670000
OTA_OFFSET=0x5D0000
fi
done
# littlefs* offset for BigDB 16mb and OTA OFFSET.
for variant in "${BIGDB_16MB[@]}"; do
if [ -z "${FILENAME##*"$variant"*}" ]; then
OFFSET=0xc90000
OTA_OFFSET=0x650000
fi
done
# Account for S3 board's different OTA partition
# FIXME: Use PlatformIO info to determine MCU type, this is unmaintainable
for variant in "${S3_VARIANTS[@]}"; do
if [ -z "${FILENAME##*"$variant"*}" ]; then
MCU="esp32s3"
fi
done
if [ "$MCU" != "esp32s3" ]; then
if [ -n "${FILENAME##*"esp32c3"*}" ]; then
OTAFILE=bleota.bin
else
OTAFILE=bleota-c3.bin
if [[ -f "$FILENAME" && "$FILENAME" == *.factory.bin ]]; then
# Display metadata if it exists
if [[ -f "$METAFILE" ]]; then
echo "Firmware metadata: ${METAFILE}"
jq . "$METAFILE"
# Extract relevant fields from metadata
if [[ $(jq -r '.part' "$METAFILE") != "null" ]]; then
OTA_OFFSET=$(jq -r '.part[] | select(.subtype == "ota_1") | .offset' "$METAFILE")
SPIFFS_OFFSET=$(jq -r '.part[] | select(.subtype == "spiffs") | .offset' "$METAFILE")
fi
MCU=$(jq -r '.mcu' "$METAFILE")
else
echo "ERROR: No metadata file found at ${METAFILE}"
exit 1
fi
# Determine OTA filename based on MCU type
if [ "$MCU" == "esp32s3" ]; then
OTAFILE=bleota-s3.bin
elif [ "$MCU" == "esp32c3" ]; then
OTAFILE=bleota-c3.bin
else
OTAFILE=bleota.bin
fi
# Set SPIFFS filename with "littlefs-" prefix.
SPIFFSFILE=littlefs-${BASENAME}
SPIFFSFILE="littlefs-${PROGNAME/firmware-/}.bin"
if [[ ! -f "$FILENAME" ]]; then
echo "Error: file ${FILENAME} wasn't found. Terminating."

View File

@@ -30,11 +30,11 @@ ECHO --change-mode Attempt to place the device in correct mode. (1200bps
ECHO Some hardware requires this twice.
ECHO.
ECHO Example: %SCRIPT_NAME% -p COM17 --change-mode
ECHO Example: %SCRIPT_NAME% -f firmware-t-deck-tft-2.6.0.0b106d4-update.bin -p COM11
ECHO Example: %SCRIPT_NAME% -f firmware-t-deck-tft-2.6.0.0b106d4.bin -p COM11
GOTO eof
:version
ECHO %SCRIPT_NAME% [Version 2.6.2]
ECHO %SCRIPT_NAME% [Version 2.7.0]
ECHO Meshtastic
GOTO eof
@@ -78,12 +78,12 @@ IF NOT EXIST !FILENAME! (
GOTO eof
)
IF "!FILENAME:update=!"=="!FILENAME!" (
CALL :LOG_MESSAGE DEBUG "We are NOT working with a *update* file. !FILENAME!"
IF NOT "__!FILENAME:.factory.bin=!__"=="__!FILENAME!__" (
CALL :LOG_MESSAGE DEBUG "We are working with a *.factory.bin* file. !FILENAME!"
CALL :LOG_MESSAGE INFO "Use script device-install.bat to flash !FILENAME!."
GOTO eof
) ELSE (
CALL :LOG_MESSAGE DEBUG "We are working with a *update* file. !FILENAME!"
CALL :LOG_MESSAGE DEBUG "We are not working with a *.factory.bin* file. !FILENAME!"
)
:skip-filename

View File

@@ -29,7 +29,7 @@ Flash image file to device, leave existing system intact."
-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 *update.bin file to flash. Custom to your device type.
-f FILENAME The *.bin file to flash. Custom to your device type.
--change-mode Attempt to place the device in correct mode. Some hardware requires this twice. (1200bps Reset)
EOF
@@ -78,7 +78,7 @@ fi
shift
}
if [ -f "${FILENAME}" ] && [ -z "${FILENAME##*"update"*}" ]; then
if [[ -f "$FILENAME" && "$FILENAME" != *.factory.bin ]]; then
echo "Trying to flash update ${FILENAME}"
$ESPTOOL_CMD --baud $FLASH_BAUD write-flash $UPDATE_OFFSET "${FILENAME}"
else

View File

@@ -75,7 +75,7 @@ TOOLS = {
}
BACKTRACE_REGEX = re.compile(
r"(?:\s+(0x40[0-2](?:\d|[a-f]|[A-F]){5}):0x(?:\d|[a-f]|[A-F]){8})\b"
r"\b(0x4[0-9a-fA-F]{7,8}):0x[0-9a-fA-F]{8}\b"
)
EXCEPTION_REGEX = re.compile("^Exception \\((?P<exc>[0-9]*)\\):$")
COUNTER_REGEX = re.compile(
@@ -89,7 +89,7 @@ POINTER_REGEX = re.compile(
STACK_BEGIN = ">>>stack>>>"
STACK_END = "<<<stack<<<"
STACK_REGEX = re.compile(
"^(?P<off>[0-9a-f]+):\W+(?P<c1>[0-9a-f]+) (?P<c2>[0-9a-f]+) (?P<c3>[0-9a-f]+) (?P<c4>[0-9a-f]+)(\W.*)?$"
r"^(?P<off>[0-9a-f]+):\W+(?P<c1>[0-9a-f]+) (?P<c2>[0-9a-f]+) (?P<c3>[0-9a-f]+) (?P<c4>[0-9a-f]+)(\W.*)?$"
)
StackLine = namedtuple("StackLine", ["offset", "content"])
@@ -223,7 +223,7 @@ class AddressResolver(object):
if match is None:
if last is not None and line.startswith("(inlined by)"):
line = line[12:].strip()
self._address_map[last] += "\n \-> inlined by: " + line
self._address_map[last] += "\n \\-> inlined by: " + line
continue
if match.group("result") == "?? ??:0":

View File

@@ -1,28 +1,32 @@
#!/usr/bin/env python
#!/usr/bin/env python3
"""Generate the CI matrix."""
import argparse
import json
import sys
import random
import re
from platformio.project.config import ProjectConfig
options = sys.argv[1:]
parser = argparse.ArgumentParser(description="Generate the CI matrix")
parser.add_argument("platform", help="Platform to build for")
parser.add_argument(
"--level",
choices=["extra", "pr"],
nargs="*",
default=[],
help="Board level to build for (omit for full release boards)",
)
args = parser.parse_args()
outlist = []
if len(options) < 1:
print(json.dumps(outlist))
exit(1)
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_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
@@ -37,36 +41,35 @@ for pio_env in pio_envs:
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))
"ci": {"board": 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:
if "check" in args.platform:
for env in all_envs:
if env['board_check']:
if "pr" in options:
if env['board_level'] == 'pr':
outlist.append(env['name'])
if env["board_check"]:
if "pr" in args.level:
if env["board_level"] == "pr":
outlist.append(env["ci"])
else:
outlist.append(env['name'])
outlist.append(env["ci"])
# Filter (non-check) builds by platform
else:
for env in all_envs:
if options[0] == env['platform']:
if args.platform == env["ci"]["platform"] or args.platform == "all":
# Always include board_level = 'pr'
if env['board_level'] == 'pr':
outlist.append(env['name'])
if env["board_level"] == "pr":
outlist.append(env["ci"])
# Include board_level = 'extra' when requested
elif "extra" in options and env['board_level'] == "extra":
outlist.append(env['name'])
elif "extra" in args.level and env["board_level"] == "extra":
outlist.append(env["ci"])
# 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'])
elif "pr" not in args.level and not env["board_level"]:
outlist.append(env["ci"])
# Return as a JSON list
print(json.dumps(outlist))

116
bin/kill-github-actions.sh Executable file
View File

@@ -0,0 +1,116 @@
#!/bin/bash
# Script to cancel all running GitHub Actions workflows
# Requires GitHub CLI (gh) to be installed and authenticated
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if gh CLI is installed
if ! command -v gh &> /dev/null; then
print_error "GitHub CLI (gh) is not installed. Please install it first:"
echo " brew install gh"
echo " Or visit: https://cli.github.com/"
exit 1
fi
# Check if authenticated
if ! gh auth status &> /dev/null; then
print_error "GitHub CLI is not authenticated. Please run:"
echo " gh auth login"
exit 1
fi
# Get repository info
REPO=$(gh repo view --json owner,name -q '.owner.login + "/" + .name')
if [[ -z "$REPO" ]]; then
print_error "Could not determine repository. Make sure you're in a GitHub repository."
exit 1
fi
print_status "Working with repository: $REPO"
# Get all active workflows (both queued and in-progress)
print_status "Fetching active workflows (queued and in-progress)..."
QUEUED_WORKFLOWS=$(gh run list --status queued --json databaseId,displayTitle,headBranch,status --limit 100)
IN_PROGRESS_WORKFLOWS=$(gh run list --status in_progress --json databaseId,displayTitle,headBranch,status --limit 100)
# Combine both lists
ALL_WORKFLOWS=$(echo "$QUEUED_WORKFLOWS $IN_PROGRESS_WORKFLOWS" | jq -s 'add | unique_by(.databaseId)')
if [[ "$ALL_WORKFLOWS" == "[]" ]]; then
print_status "No active workflows found."
exit 0
fi
# Parse and display active workflows
echo
print_warning "Found active workflows:"
echo "$ALL_WORKFLOWS" | jq -r '.[] | " - \(.displayTitle) (Branch: \(.headBranch), Status: \(.status), ID: \(.databaseId))"'
echo
read -p "Do you want to cancel ALL these workflows? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_status "Cancelled by user."
exit 0
fi
# Cancel each workflow
print_status "Cancelling workflows..."
CANCELLED_COUNT=0
FAILED_COUNT=0
while IFS= read -r WORKFLOW_ID; do
if [[ -n "$WORKFLOW_ID" ]]; then
print_status "Cancelling workflow ID: $WORKFLOW_ID"
if gh run cancel "$WORKFLOW_ID" 2>/dev/null; then
((CANCELLED_COUNT++))
else
print_error "Failed to cancel workflow ID: $WORKFLOW_ID"
((FAILED_COUNT++))
fi
fi
done < <(echo "$ALL_WORKFLOWS" | jq -r '.[].databaseId')
echo
print_status "Summary:"
echo " - Cancelled: $CANCELLED_COUNT workflows"
if [[ $FAILED_COUNT -gt 0 ]]; then
echo " - Failed: $FAILED_COUNT workflows"
fi
print_status "Done!"
# Optional: Show remaining active workflows
echo
print_status "Checking for any remaining active workflows..."
REMAINING_QUEUED=$(gh run list --status queued --json databaseId --limit 10)
REMAINING_IN_PROGRESS=$(gh run list --status in_progress --json databaseId --limit 10)
REMAINING_ALL=$(echo "$REMAINING_QUEUED $REMAINING_IN_PROGRESS" | jq -s 'add | unique_by(.databaseId)')
if [[ "$REMAINING_ALL" == "[]" ]]; then
print_status "All workflows successfully cancelled."
else
REMAINING_COUNT=$(echo "$REMAINING_ALL" | jq '. | length')
print_warning "Still $REMAINING_COUNT workflows active (may take a moment to update status)"
fi

View File

@@ -2,4 +2,4 @@
set -e
pio run --environment native
gdbserver --once localhost:2345 .pio/build/native/program "$@"
gdbserver --once localhost:2345 .pio/build/native/meshtasticd "$@"

View File

@@ -2,4 +2,4 @@
set -e
pio run --environment native
.pio/build/native/program "$@"
.pio/build/native/meshtasticd "$@"

View File

@@ -87,6 +87,21 @@
</screenshots>
<releases>
<release version="2.7.17" date="2025-11-28">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.17</url>
</release>
<release version="2.7.16" date="2025-11-19">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.16</url>
</release>
<release version="2.7.15" date="2025-11-13">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.15</url>
</release>
<release version="2.7.14" date="2025-11-03">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.14</url>
</release>
<release version="2.7.13" date="2025-10-11">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.13</url>
</release>
<release version="2.7.12" date="2025-10-01">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.12</url>
</release>

View File

@@ -2,98 +2,77 @@
# trunk-ignore-all(ruff/F821)
# trunk-ignore-all(flake8/F821): For SConstruct imports
import sys
from os.path import join
from os.path import join, basename, isfile
import subprocess
import json
import re
import time
from datetime import datetime
from readprops import readProps
Import("env")
platform = env.PioPlatform()
progname = env.get("PROGNAME")
lfsbin = f"{progname.replace('firmware-', 'littlefs-')}.bin"
def esp32_create_combined_bin(source, target, env):
# this sub is borrowed from ESPEasy build toolchain. It's licensed under GPL V3
# https://github.com/letscontrolit/ESPEasy/blob/mega/tools/pio/post_esp32.py
print("Generating combined binary for serial flashing")
app_offset = 0x10000
new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.factory.bin")
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin")
chip = env.get("BOARD_MCU")
flash_size = env.BoardConfig().get("upload.flash_size")
flash_freq = env.BoardConfig().get("build.f_flash", "40m")
flash_freq = flash_freq.replace("000000L", "m")
flash_mode = env.BoardConfig().get("build.flash_mode", "dio")
memory_type = env.BoardConfig().get("build.arduino.memory_type", "qio_qspi")
if flash_mode == "qio" or flash_mode == "qout":
flash_mode = "dio"
if memory_type == "opi_opi" or memory_type == "opi_qspi":
flash_mode = "dout"
cmd = [
"--chip",
chip,
"merge_bin",
"-o",
new_file_name,
"--flash_mode",
flash_mode,
"--flash_freq",
flash_freq,
"--flash_size",
flash_size,
def manifest_gather(source, target, env):
out = []
check_paths = [
progname,
f"{progname}.elf",
f"{progname}.bin",
f"{progname}.factory.bin",
f"{progname}.hex",
f"{progname}.merged.hex",
f"{progname}.uf2",
f"{progname}.factory.uf2",
f"{progname}.zip",
lfsbin
]
for p in check_paths:
f = env.File(env.subst(f"$BUILD_DIR/{p}"))
if f.exists():
d = {
"name": p,
"md5": f.get_content_hash(), # Returns MD5 hash
"bytes": f.get_size() # Returns file size in bytes
}
out.append(d)
print(d)
manifest_write(out, env)
print(" Offset | File")
for section in sections:
sect_adr, sect_file = section.split(" ", 1)
print(f" - {sect_adr} | {sect_file}")
cmd += [sect_adr, sect_file]
def manifest_write(files, env):
manifest = {
"version": verObj["long"],
"build_epoch": build_epoch,
"board": env.get("PIOENV"),
"mcu": env.get("BOARD_MCU"),
"repo": repo_owner,
"files": files,
"part": None,
"has_mui": False,
"has_inkhud": False,
}
# Get partition table (generated in esp32_pre.py) if it exists
if env.get("custom_mtjson_part"):
# custom_mtjson_part is a JSON string, convert it back to a dict
pj = json.loads(env.get("custom_mtjson_part"))
manifest["part"] = pj
# Enable has_mui for TFT builds
if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
manifest["has_mui"] = True
if "MESHTASTIC_INCLUDE_INKHUD" in env.get("CPPDEFINES", []):
manifest["has_inkhud"] = True
print(f" - {hex(app_offset)} | {firmware_name}")
cmd += [hex(app_offset), firmware_name]
print("Using esptool.py arguments: %s" % " ".join(cmd))
esptool.main(cmd)
if platform.name == "espressif32":
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
import esptool
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)
esp32_kind = env.GetProjectOption("custom_esp32_kind")
if esp32_kind == "esp32":
# Free up some IRAM by removing auxiliary SPI flash chip drivers.
# Wrapped stub symbols are defined in src/platform/esp32/iram-quirk.c.
env.Append(
LINKFLAGS=[
"-Wl,--wrap=esp_flash_chip_gd",
"-Wl,--wrap=esp_flash_chip_issi",
"-Wl,--wrap=esp_flash_chip_winbond",
]
)
else:
# For newer ESP32 targets, using newlib nano works better.
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
if platform.name == "nordicnrf52":
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex",
env.VerboseAction(f"\"{sys.executable}\" ./bin/uf2conv.py \"$BUILD_DIR/firmware.hex\" -c -f 0xADA52840 -o \"$BUILD_DIR/firmware.uf2\"",
"Generating UF2 file"))
# Write the manifest to the build directory
with open(env.subst("$BUILD_DIR/${PROGNAME}.mt.json"), "w") as f:
json.dump(manifest, f, indent=2)
Import("projenv")
prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
verObj = readProps(prefsLoc)
print("Using meshtastic platformio-custom.py, firmware version " + verObj["long"] + " on " + env.get("PIOENV"))
print(f"Using meshtastic platformio-custom.py, firmware version {verObj['long']} on {env.get('PIOENV')}")
# get repository owner if git is installed
try:
@@ -139,10 +118,10 @@ flags = [
"-DBUILD_EPOCH=" + str(build_epoch),
] + pref_flags
print ("Using flags:")
print("Using flags:")
for flag in flags:
print(flag)
projenv.Append(
CCFLAGS=flags,
)
@@ -180,4 +159,22 @@ def load_boot_logo(source, target, env):
# Load the boot logo on TFT builds
if ("HAS_TFT", 1) in env.get("CPPDEFINES", []):
env.AddPreAction('$BUILD_DIR/littlefs.bin', load_boot_logo)
env.AddPreAction(f"$BUILD_DIR/{lfsbin}", load_boot_logo)
mtjson_deps = ["buildprog"]
if platform.name == "espressif32":
# Build littlefs image as part of mtjson target
# Equivalent to `pio run -t buildfs`
target_lfs = env.DataToBin(
join("$BUILD_DIR", "${ESP32_FS_IMAGE_NAME}"), "$PROJECT_DATA_DIR"
)
mtjson_deps.append(target_lfs)
env.AddCustomTarget(
name="mtjson",
dependencies=mtjson_deps,
actions=[manifest_gather],
title="Meshtastic Manifest",
description="Generating Meshtastic manifest JSON + Checksums",
always_build=False,
)

19
bin/platformio-pre.py Normal file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env python3
# trunk-ignore-all(ruff/F821)
# trunk-ignore-all(flake8/F821): For SConstruct imports
Import("env")
platform = env.PioPlatform()
if platform.name == "native":
env.Replace(PROGNAME="meshtasticd")
else:
from readprops import readProps
prefsLoc = env["PROJECT_DIR"] + "/version.properties"
verObj = readProps(prefsLoc)
env.Replace(PROGNAME=f"firmware-{env.get('PIOENV')}-{verObj['long']}")
env.Replace(ESP32_FS_IMAGE_NAME=f"littlefs-{env.get('PIOENV')}-{verObj['long']}")
# Print the new program name for verification
print(f"PROGNAME: {env.get('PROGNAME')}")
if platform.name == "espressif32":
print(f"ESP32_FS_IMAGE_NAME: {env.get('ESP32_FS_IMAGE_NAME')}")

View File

@@ -3,7 +3,7 @@
set -e
echo "Starting simulator"
.pio/build/native/program &
.pio/build/native/meshtasticd -s &
sleep 20 # 5 seconds was not enough
echo "Simulator started, launching python test..."

View File

@@ -1 +1 @@
2.6.4
2.6.7

53
boards/ThinkNode-M3.json Normal file
View File

@@ -0,0 +1,53 @@
{
"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"]
],
"usb_product": "elecrow_eink",
"mcu": "nrf52840",
"variant": "ELECROW-ThinkNode-M3",
"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": "elecrow nrf",
"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": "",
"vendor": "ELECROW"
}

53
boards/ThinkNode-M6.json Normal file
View File

@@ -0,0 +1,53 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_ELECROW_M6 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
["0x239A", "0x002A"]
],
"usb_product": "elecrow_thinknode_m6",
"mcu": "nrf52840",
"variant": "ELECROW-ThinkNode-M6",
"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": "ELECROW ThinkNode M6",
"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://www.elecrow.com/thinknode-m6-outdoor-solar-power-for-lora-powered-by-nrf52840-supports-gps.html",
"vendor": "ELECROW"
}

View File

@@ -0,0 +1,41 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "hackaday-communicator"
},
"connectivity": ["wifi", "bluetooth", "lora"],
"debug": {
"default_tool": "esp-builtin",
"onboard_tools": ["esp-builtin"],
"openocd_target": "esp32s3.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "hackaday-communicator (16 MB FLASH, 8 MB PSRAM)",
"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": 1500000
},
"url": "hackaday.com",
"vendor": "hackaday"
}

View File

@@ -9,7 +9,7 @@
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],

56
boards/muzi-base.json Normal file
View File

@@ -0,0 +1,56 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_MUZI_BASE -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [["0x239A", "0xcafe"]],
"mcu": "nrf52840",
"variant": "muzi-base",
"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": "Muzi Base",
"url": "https://muzi.works/",
"vendor": "MuziWorks",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"blackmagic",
"cmsis-dap",
"mbed",
"stlink"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
}
}

31
debian/changelog vendored
View File

@@ -1,3 +1,34 @@
meshtasticd (2.7.17.0) unstable; urgency=medium
* Version 2.7.17
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Fri, 28 Nov 2025 15:11:34 +0000
meshtasticd (2.7.16.0) unstable; urgency=medium
* Version 2.7.16
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Wed, 19 Nov 2025 16:12:32 +0000
meshtasticd (2.7.15.0) unstable; urgency=medium
* Version 2.7.15
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Thu, 13 Nov 2025 12:31:57 +0000
meshtasticd (2.7.14.0) unstable; urgency=medium
* Version 2.7.14
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Mon, 03 Nov 2025 16:11:31 +0000
meshtasticd (2.7.13.0) unstable; urgency=medium
* Version 2.7.13
-- GitHub Actions <github-actions[bot]@users.noreply.github.com> Sat, 11 Oct 2025 15:27:28 +0000
meshtasticd (2.7.12.0) unstable; urgency=medium
[ Austin Lane ]

1
debian/control vendored
View File

@@ -3,6 +3,7 @@ Section: misc
Priority: optional
Maintainer: Austin Lane <vidplace7@gmail.com>
Build-Depends: debhelper-compat (= 13),
libc6-dev (>= 2.38) | libbsd-dev,
lsb-release,
tar,
gzip,

1
debian/rules vendored
View File

@@ -28,5 +28,4 @@ override_dh_auto_build:
# Build with platformio
$(PIO_ENV) platformio run -e native-tft
# Move the binary and default config to the correct name
mv .pio/build/native-tft/program .pio/build/native-tft/meshtasticd
cp bin/config-dist.yaml bin/config.yaml

View File

@@ -1,10 +1,9 @@
#!/usr/bin/env python3
# trunk-ignore-all(flake8/F821)
# trunk-ignore-all(ruff/F821)
Import("env")
# NOTE: This is not currently used, but can serve as an example on how to write extra_scripts
# print("Current CLI targets", COMMAND_LINE_TARGETS)
# print("Current Build targets", BUILD_TARGETS)
# print("CPP defs", env.get("CPPDEFINES"))

86
extra_scripts/esp32_extra.py Executable file
View File

@@ -0,0 +1,86 @@
#!/usr/bin/env python3
# trunk-ignore-all(ruff/F821)
# trunk-ignore-all(flake8/F821): For SConstruct imports
# trunk-ignore-all(ruff/E402): Hacky esptool import
# trunk-ignore-all(flake8/E402): Hacky esptool import
import sys
from os.path import join
Import("env")
platform = env.PioPlatform()
sys.path.append(join(platform.get_package_dir("tool-esptoolpy")))
# IntelHex workaround, remove after fixed upstream
# https://github.com/platformio/platform-espressif32/issues/1632
try:
import intelhex
except ImportError:
env.Execute("$PYTHONEXE -m pip install intelhex")
import esptool
def esp32_create_combined_bin(source, target, env):
# this sub is borrowed from ESPEasy build toolchain. It's licensed under GPL V3
# https://github.com/letscontrolit/ESPEasy/blob/mega/tools/pio/post_esp32.py
print("Generating combined binary for serial flashing")
app_offset = 0x10000
new_file_name = env.subst("$BUILD_DIR/${PROGNAME}.factory.bin")
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin")
chip = env.get("BOARD_MCU")
board = env.BoardConfig()
flash_size = board.get("upload.flash_size")
flash_freq = board.get("build.f_flash", "40m")
flash_freq = flash_freq.replace("000000L", "m")
flash_mode = board.get("build.flash_mode", "dio")
memory_type = board.get("build.arduino.memory_type", "qio_qspi")
if flash_mode == "qio" or flash_mode == "qout":
flash_mode = "dio"
if memory_type == "opi_opi" or memory_type == "opi_qspi":
flash_mode = "dout"
cmd = [
"--chip",
chip,
"merge_bin",
"-o",
new_file_name,
"--flash_mode",
flash_mode,
"--flash_freq",
flash_freq,
"--flash_size",
flash_size,
]
print(" Offset | File")
for section in sections:
sect_adr, sect_file = section.split(" ", 1)
print(f" - {sect_adr} | {sect_file}")
cmd += [sect_adr, sect_file]
print(f" - {hex(app_offset)} | {firmware_name}")
cmd += [hex(app_offset), firmware_name]
print("Using esptool.py arguments: %s" % " ".join(cmd))
esptool.main(cmd)
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin)
esp32_kind = env.GetProjectOption("custom_esp32_kind")
if esp32_kind == "esp32":
# Free up some IRAM by removing auxiliary SPI flash chip drivers.
# Wrapped stub symbols are defined in src/platform/esp32/iram-quirk.c.
env.Append(
LINKFLAGS=[
"-Wl,--wrap=esp_flash_chip_gd",
"-Wl,--wrap=esp_flash_chip_issi",
"-Wl,--wrap=esp_flash_chip_winbond",
]
)
else:
# For newer ESP32 targets, using newlib nano works better.
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])

73
extra_scripts/esp32_pre.py Executable file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/env python3
# trunk-ignore-all(ruff/F821)
# trunk-ignore-all(flake8/F821): For SConstruct imports
import json
import sys
from os.path import isfile
Import("env")
# From https://github.com/platformio/platform-espressif32/blob/develop/builder/main.py
def _parse_size(value):
if isinstance(value, int):
return value
elif value.isdigit():
return int(value)
elif value.startswith("0x"):
return int(value, 16)
elif value[-1].upper() in ("K", "M"):
base = 1024 if value[-1].upper() == "K" else 1024 * 1024
return int(value[:-1]) * base
return value
def _parse_partitions(env):
partitions_csv = env.subst("$PARTITIONS_TABLE_CSV")
if not isfile(partitions_csv):
sys.stderr.write(
"Could not find the file %s with partitions " "table.\n" % partitions_csv
)
env.Exit(1)
return
result = []
# The first offset is 0x9000 because partition table is flashed to 0x8000 and
# occupies an entire flash sector, which size is 0x1000
next_offset = 0x9000
with open(partitions_csv) as fp:
for line in fp.readlines():
line = line.strip()
if not line or line.startswith("#"):
continue
tokens = [t.strip() for t in line.split(",")]
if len(tokens) < 5:
continue
bound = 0x10000 if tokens[1] in ("0", "app") else 4
calculated_offset = (next_offset + bound - 1) & ~(bound - 1)
partition = {
"name": tokens[0],
"type": tokens[1],
"subtype": tokens[2],
"offset": tokens[3] or calculated_offset,
"size": tokens[4],
"flags": tokens[5] if len(tokens) > 5 else None,
}
result.append(partition)
next_offset = _parse_size(partition["offset"]) + _parse_size(
partition["size"]
)
return result
def mtjson_esp32_part(target, source, env):
part = _parse_partitions(env)
pj = json.dumps(part)
# print(f"JSON_PARTITIONS: {pj}")
# Dump json string to 'custom_mtjson_part' variable to use later when writing the manifest
env.Replace(custom_mtjson_part=pj)
env.AddPreAction("mtjson", mtjson_esp32_part)

50
extra_scripts/nrf52_extra.py Executable file
View File

@@ -0,0 +1,50 @@
#!/usr/bin/env python3
# trunk-ignore-all(ruff/F821)
# trunk-ignore-all(flake8/F821): For SConstruct imports
import sys
from os.path import basename
Import("env")
# Custom HEX from ELF
# Convert hex to uf2 for nrf52
def nrf52_hex_to_uf2(source, target, env):
hex_path = target[0].get_abspath()
# When using merged hex, drop 'merged' from uf2 filename
uf2_path = hex_path.replace(".merged.", ".")
uf2_path = uf2_path.replace(".hex", ".uf2")
env.Execute(
env.VerboseAction(
f'"{sys.executable}" ./bin/uf2conv.py "{hex_path}" -c -f 0xADA52840 -o "{uf2_path}"',
f"Generating UF2 file from {basename(hex_path)}",
)
)
def nrf52_mergehex(source, target, env):
hex_path = target[0].get_abspath()
merged_hex_path = hex_path.replace(".hex", ".merged.hex")
merge_with = None
if "wio-sdk-wm1110" == str(env.get("PIOENV")):
merge_with = env.subst("$PROJECT_DIR/bin/s140_nrf52_7.3.0_softdevice.hex")
else:
print("merge_with not defined for this target")
if merge_with is not None:
env.Execute(
env.VerboseAction(
f'"$PROJECT_DIR/bin/mergehex" -m "{hex_path}" "{merge_with}" -o "{merged_hex_path}"',
"Merging HEX with SoftDevice",
)
)
print(f'Merged file saved at "{basename(merged_hex_path)}"')
nrf52_hex_to_uf2([hex_path, merge_with], [env.File(merged_hex_path)], env)
# if WM1110 target, merge hex with softdevice 7.3.0
if "wio-sdk-wm1110" == env.get("PIOENV"):
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", nrf52_mergehex)
else:
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex", nrf52_hex_to_uf2)

View File

@@ -1,7 +1,9 @@
#!/usr/bin/env python3
# trunk-ignore-all(ruff/F821)
# trunk-ignore-all(flake8/F821): For SConstruct imports
Import("env")
# Custom HEX from ELF
env.AddPostAction(
"$BUILD_DIR/${PROGNAME}.elf",

View File

@@ -49,6 +49,13 @@ BuildRequires: pkgconfig(x11)
BuildRequires: pkgconfig(libinput)
BuildRequires: pkgconfig(xkbcommon-x11)
# libbsd is needed on older Fedora/RHEL to provide 'strlcpy'
%if 0%{?fedora} >= 39 || 0%{?rhel} >= 10
BuildRequires: glibc-devel >= 2.38
%else
BuildRequires: pkgconfig(libbsd-overlay)
%endif
Requires: systemd-udev
%description
@@ -69,7 +76,7 @@ platformio run -e native-tft
%install
# Install meshtasticd binary
mkdir -p %{buildroot}%{_bindir}
install -m 0755 .pio/build/native-tft/program %{buildroot}%{_bindir}/meshtasticd
install -m 0755 .pio/build/native-tft/meshtasticd %{buildroot}%{_bindir}/meshtasticd
# Install portduino VFS dir
install -p -d -m 0770 %{buildroot}%{_localstatedir}/lib/meshtasticd

View File

@@ -5,7 +5,7 @@
default_envs = tbeam
extra_configs =
arch/*/*.ini
variants/*/*.ini
variants/*/*/platformio.ini
variants/*/diy/*/platformio.ini
src/graphics/niche/InkHUD/PlatformioConfig.ini
@@ -14,7 +14,9 @@ description = Meshtastic
[env]
test_build_src = true
extra_scripts = bin/platformio-custom.py
extra_scripts =
pre:bin/platformio-pre.py
bin/platformio-custom.py
; note: we add src to our include search path so that lmic_project_config can override
; note: TINYGPS_OPTION_NO_CUSTOM_FIELDS is VERY important. We don't use custom fields and somewhere in that pile
; of code is a heap corruption bug!
@@ -62,7 +64,7 @@ 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/0cbc26b1f8f61957af0475f486b362eafe7cc4e2.zip
https://github.com/meshtastic/esp8266-oled-ssd1306/archive/2887bf4a19f64d92c984dcc8fd5ca7429e425e4a.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
@@ -70,7 +72,7 @@ lib_deps =
# renovate: datasource=git-refs depName=meshtastic-TinyGPSPlus packageName=https://github.com/meshtastic/TinyGPSPlus gitBranch=master
https://github.com/meshtastic/TinyGPSPlus/archive/71a82db35f3b973440044c476d4bcdc673b104f4.zip
# renovate: datasource=git-refs depName=meshtastic-ArduinoThread packageName=https://github.com/meshtastic/ArduinoThread gitBranch=master
https://github.com/meshtastic/ArduinoThread/archive/7c3ee9e1951551b949763b1f5280f8db1fa4068d.zip
https://github.com/meshtastic/ArduinoThread/archive/b841b0415721f1341ea41cccfb4adccfaf951567.zip
# renovate: datasource=custom.pio depName=Nanopb packageName=nanopb/library/Nanopb
nanopb/Nanopb@0.4.91
# renovate: datasource=custom.pio depName=ErriezCRC32 packageName=erriez/library/ErriezCRC32
@@ -90,7 +92,7 @@ framework = arduino
lib_deps =
${env.lib_deps}
# renovate: datasource=custom.pio depName=NonBlockingRTTTL packageName=end2endzone/library/NonBlockingRTTTL
end2endzone/NonBlockingRTTTL@1.3.0
end2endzone/NonBlockingRTTTL@1.4.0
build_flags = ${env.build_flags} -Os
build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/>
@@ -115,12 +117,13 @@ lib_deps =
[radiolib_base]
lib_deps =
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
jgromes/RadioLib@7.3.0
# jgromes/RadioLib@7.4.0
https://github.com/jgromes/RadioLib/archive/536c7267362e2c1345be7054ba45e503252975ff.zip
[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/505ffadaa7a931df5dc8153229b719a07bbb028c.zip
https://github.com/meshtastic/device-ui/archive/2746a1ce3804998460a2cb319b8ea8a238dfd8c9.zip
; Common libs for environmental measurements in telemetry module
[environmental_base]
@@ -164,11 +167,11 @@ lib_deps =
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
mprograms/QMC5883LCompass@1.2.3
# renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU
dfrobot/DFRobot_RTU@1.0.3
dfrobot/DFRobot_RTU@1.0.6
# renovate: datasource=git-refs depName=DFRobot_RainfallSensor packageName=https://github.com/DFRobot/DFRobot_RainfallSensor gitBranch=master
https://github.com/DFRobot/DFRobot_RainfallSensor/archive/38fea5e02b40a5430be6dab39a99a6f6347d667e.zip
# renovate: datasource=custom.pio depName=INA226 packageName=robtillaart/library/INA226
robtillaart/INA226@0.6.4
robtillaart/INA226@0.6.5
# renovate: datasource=custom.pio depName=SparkFun MAX3010x packageName=sparkfun/library/SparkFun MAX3010x Pulse and Proximity Sensor Library
sparkfun/SparkFun MAX3010x Pulse and Proximity Sensor Library@1.1.2
# renovate: datasource=custom.pio depName=SparkFun 9DoF IMU Breakout ICM 20948 packageName=sparkfun/library/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library
@@ -176,11 +179,13 @@ lib_deps =
# renovate: datasource=custom.pio depName=Adafruit LTR390 Library packageName=adafruit/library/Adafruit LTR390 Library
adafruit/Adafruit LTR390 Library@1.1.2
# renovate: datasource=custom.pio depName=Adafruit PCT2075 packageName=adafruit/library/Adafruit PCT2075
adafruit/Adafruit PCT2075@1.0.5
adafruit/Adafruit PCT2075@1.0.6
# renovate: datasource=custom.pio depName=DFRobot_BMM150 packageName=dfrobot/library/DFRobot_BMM150
dfrobot/DFRobot_BMM150@1.0.0
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
adafruit/Adafruit TSL2561@1.1.2
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/library/BH1750_WE
wollewald/BH1750_WE@1.1.10
; (not included in native / portduino)
[environmental_extra]
@@ -202,7 +207,7 @@ lib_deps =
# renovate: datasource=custom.pio depName=SparkFun Qwiic Scale NAU7802 packageName=sparkfun/library/SparkFun Qwiic Scale NAU7802 Arduino Library
sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6
# renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
ClosedCube OPT3001@1.1.2
closedcube/ClosedCube OPT3001@1.1.2
# renovate: datasource=custom.pio depName=Bosch BSEC2 packageName=boschsensortec/library/bsec2
boschsensortec/bsec2@1.10.2610
# renovate: datasource=custom.pio depName=Bosch BME68x packageName=boschsensortec/library/BME68x Sensor Library
@@ -210,6 +215,6 @@ lib_deps =
# 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
# renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
sensirion/Sensirion Core@0.7.1
sensirion/Sensirion Core@0.7.2
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
sensirion/Sensirion I2C SCD4x@1.1.0

View File

@@ -50,8 +50,11 @@ class AudioThread : public concurrency::OSThread
delete i2sRtttl;
i2sRtttl = nullptr;
}
delete rtttlFile;
rtttlFile = nullptr;
if (rtttlFile != nullptr) {
delete rtttlFile;
rtttlFile = nullptr;
}
setCPUFast(false);
#ifdef T_LORA_PAGER
@@ -99,9 +102,9 @@ class AudioThread : public concurrency::OSThread
};
AudioGeneratorRTTTL *i2sRtttl = nullptr;
AudioOutputI2S *audioOut;
AudioOutputI2S *audioOut = nullptr;
AudioFileSourcePROGMEM *rtttlFile;
AudioFileSourcePROGMEM *rtttlFile = nullptr;
};
#endif

View File

@@ -14,16 +14,16 @@ class NodeStatus : public Status
CallbackObserver<NodeStatus, const NodeStatus *> statusObserver =
CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
uint8_t numOnline = 0;
uint8_t numTotal = 0;
uint16_t numOnline = 0;
uint16_t numTotal = 0;
uint8_t lastNumTotal = 0;
uint16_t lastNumTotal = 0;
public:
bool forceUpdate = false;
NodeStatus() { statusType = STATUS_TYPE_NODE; }
NodeStatus(uint8_t numOnline, uint8_t numTotal, bool forceUpdate = false) : Status()
NodeStatus(uint16_t numOnline, uint16_t numTotal, bool forceUpdate = false) : Status()
{
this->forceUpdate = forceUpdate;
this->numOnline = numOnline;
@@ -34,11 +34,11 @@ class NodeStatus : public Status
void observe(Observable<const NodeStatus *> *source) { statusObserver.observe(source); }
uint8_t getNumOnline() const { return numOnline; }
uint16_t getNumOnline() const { return numOnline; }
uint8_t getNumTotal() const { return numTotal; }
uint16_t getNumTotal() const { return numTotal; }
uint8_t getLastNumTotal() const { return lastNumTotal; }
uint16_t getLastNumTotal() const { return lastNumTotal; }
bool matches(const NodeStatus *newStatus) const
{
@@ -56,7 +56,7 @@ class NodeStatus : public Status
numTotal = newStatus->getNumTotal();
}
if (isDirty || newStatus->forceUpdate) {
LOG_DEBUG("Node status update: %d online, %d total", numOnline, numTotal);
LOG_DEBUG("Node status update: %u online, %u total", numOnline, numTotal);
onNewStatus.notifyObservers(this);
}
return 0;

View File

@@ -194,7 +194,7 @@ static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level se
#ifdef BATTERY_PIN
static void adcEnable()
void battery_adcEnable()
{
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP
@@ -214,7 +214,7 @@ static void adcEnable()
#endif
}
static void adcDisable()
static void battery_adcDisable()
{
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP
@@ -278,6 +278,11 @@ class AnalogBatteryLevel : public HasBatteryLevel
break;
}
}
#if defined(BATTERY_CHARGING_INV)
// bit of trickery to show 99% up until the charge finishes
if (!digitalRead(BATTERY_CHARGING_INV) && battery_SOC > 99)
battery_SOC = 99;
#endif
return clamp((int)(battery_SOC), 0, 100);
}
@@ -320,7 +325,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
uint32_t raw = 0;
float scaled = 0;
adcEnable();
battery_adcEnable();
#ifdef ARCH_ESP32 // ADC block for espressif platforms
raw = espAdcRead();
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
@@ -332,7 +337,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
raw = raw / BATTERY_SENSE_SAMPLES;
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
#endif
adcDisable();
battery_adcDisable();
if (!initial_read_done) {
// Flush the smoothing filter with an ADC reading, if the reading is plausibly correct
@@ -455,6 +460,8 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
// if it's not HIGH - check the battery
#endif
#elif defined(MUZI_BASE)
return NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk;
#endif
return getBattVoltage() > chargingVolt;
}
@@ -470,6 +477,8 @@ class AnalogBatteryLevel : public HasBatteryLevel
#endif
#ifdef EXT_CHRG_DETECT
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
#elif defined(BATTERY_CHARGING_INV)
return !digitalRead(BATTERY_CHARGING_INV);
#else
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
if (hasINA()) {
@@ -562,6 +571,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
config.power.device_battery_ina_address) {
if (!ina226Sensor.isInitialized())
return ina226Sensor.runOnce() > 0;
return ina226Sensor.isRunning();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
config.power.device_battery_ina_address) {
if (!ina260Sensor.isInitialized())
@@ -691,7 +701,24 @@ bool Power::setup()
#ifdef NRF_APM
found = true;
#endif
#ifdef EXT_PWR_DETECT
attachInterrupt(
EXT_PWR_DETECT,
[]() {
power->setIntervalFromNow(0);
runASAP = true;
},
CHANGE);
#endif
#ifdef BATTERY_CHARGING_INV
attachInterrupt(
BATTERY_CHARGING_INV,
[]() {
power->setIntervalFromNow(0);
runASAP = true;
},
CHANGE);
#endif
enabled = found;
low_voltage_counter = 0;
@@ -748,6 +775,8 @@ void Power::shutdown()
if (screen) {
#ifdef T_DECK_PRO
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button
#elif defined(USE_EINK)
screen->showSimpleBanner("Shutting Down...", 2250); // dismiss after 3 seconds to avoid the banner on the sleep screen
#else
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
#endif
@@ -828,8 +857,11 @@ void Power::readPowerStatus()
// Notify any status instances that are observing us
const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isChargingNow, batteryVoltageMv, batteryChargePercent);
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(), powerStatus2.getIsCharging(),
powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
if (millis() > lastLogTime + 50 * 1000) {
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(),
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
lastLogTime = millis();
}
newStatus.notifyObservers(&powerStatus2);
#ifdef DEBUG_HEAP
if (lastheap != memGet.getFreeHeap()) {
@@ -892,13 +924,8 @@ void Power::readPowerStatus()
low_voltage_counter++;
LOG_DEBUG("Low voltage counter: %d/10", low_voltage_counter);
if (low_voltage_counter > 10) {
#ifdef ARCH_NRF52
// We can't trigger deep sleep on NRF52, it's freezing the board
LOG_DEBUG("Low voltage detected, but not trigger deep sleep");
#else
LOG_INFO("Low voltage detected, trigger deep sleep");
powerFSM.trigger(EVENT_LOW_BATTERY);
#endif
}
} else {
low_voltage_counter = 0;
@@ -1426,7 +1453,7 @@ class LipoCharger : public HasBatteryLevel
/**
* return true if there is an external power source detected
*/
virtual bool isVbusIn() override { return PPM->getVbusVoltage() > 0; }
virtual bool isVbusIn() override { return PPM->isVbusIn(); }
/**
* return true if the battery is currently charging
@@ -1538,4 +1565,4 @@ bool Power::meshSolarInit()
{
return false;
}
#endif
#endif

View File

@@ -57,21 +57,21 @@ static bool isPowered()
static void sdsEnter()
{
LOG_DEBUG("State: SDS");
LOG_POWERFSM("State: SDS");
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, false);
}
static void lowBattSDSEnter()
{
LOG_DEBUG("State: Lower batt SDS");
LOG_POWERFSM("State: Lower batt SDS");
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, true);
}
extern Power *power;
static void shutdownEnter()
{
LOG_DEBUG("State: SHUTDOWN");
LOG_POWERFSM("State: SHUTDOWN");
shutdownAtMsec = millis();
}
@@ -81,7 +81,7 @@ static uint32_t secsSlept;
static void lsEnter()
{
LOG_INFO("lsEnter begin, ls_secs=%u", config.power.ls_secs);
LOG_POWERFSM("lsEnter begin, ls_secs=%u", config.power.ls_secs);
if (screen)
screen->setOn(false);
secsSlept = 0; // How long have we been sleeping this time
@@ -155,12 +155,12 @@ static void lsIdle()
static void lsExit()
{
LOG_INFO("Exit state: LS");
LOG_POWERFSM("State: lsExit");
}
static void nbEnter()
{
LOG_DEBUG("State: NB");
LOG_POWERFSM("State: nbEnter");
if (screen)
screen->setOn(false);
#ifdef ARCH_ESP32
@@ -173,6 +173,7 @@ static void nbEnter()
static void darkEnter()
{
LOG_POWERFSM("State: darkEnter");
setBluetoothEnable(true);
if (screen)
screen->setOn(false);
@@ -180,7 +181,7 @@ static void darkEnter()
static void serialEnter()
{
LOG_DEBUG("State: SERIAL");
LOG_POWERFSM("State: serialEnter");
setBluetoothEnable(false);
if (screen) {
screen->setOn(true);
@@ -189,13 +190,14 @@ static void serialEnter()
static void serialExit()
{
LOG_POWERFSM("State: serialExit");
// Turn bluetooth back on when we leave serial stream API
setBluetoothEnable(true);
}
static void powerEnter()
{
// LOG_DEBUG("State: POWER");
LOG_POWERFSM("State: powerEnter");
if (!isPowered()) {
// If we got here, we are in the wrong state - we should be in powered, let that state handle things
LOG_INFO("Loss of power in Powered");
@@ -210,6 +212,7 @@ static void powerEnter()
static void powerIdle()
{
// LOG_POWERFSM("State: powerIdle"); // very chatty
if (!isPowered()) {
// If we got here, we are in the wrong state
LOG_INFO("Loss of power in Powered");
@@ -219,14 +222,13 @@ static void powerIdle()
static void powerExit()
{
if (screen)
screen->setOn(true);
LOG_POWERFSM("State: powerExit");
setBluetoothEnable(true);
}
static void onEnter()
{
LOG_DEBUG("State: ON");
LOG_POWERFSM("State: onEnter");
if (screen)
screen->setOn(true);
setBluetoothEnable(true);
@@ -234,6 +236,7 @@ static void onEnter()
static void onIdle()
{
LOG_POWERFSM("State: onIdle");
if (isPowered()) {
// If we got here, we are in the wrong state - we should be in powered, let that state handle things
powerFSM.trigger(EVENT_POWER_CONNECTED);
@@ -242,7 +245,7 @@ static void onIdle()
static void bootEnter()
{
LOG_DEBUG("State: BOOT");
LOG_POWERFSM("State: bootEnter");
}
State stateSHUTDOWN(shutdownEnter, NULL, NULL, "SHUTDOWN");
@@ -319,11 +322,6 @@ void PowerFSM_setup()
// if any packet destined for phone arrives, turn on bluetooth at least
powerFSM.add_transition(&stateNB, &stateDARK, EVENT_PACKET_FOR_PHONE, NULL, "Packet for phone");
// Removed 2.7: we don't show the nodes individually for every node on the screen anymore
// powerFSM.add_transition(&stateNB, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
// powerFSM.add_transition(&stateDARK, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
// powerFSM.add_transition(&stateON, &stateON, EVENT_NODEDB_UPDATED, NULL, "NodeDB update");
// Show the received text message
powerFSM.add_transition(&stateLS, &stateON, EVENT_RECEIVED_MSG, NULL, "Received text");
powerFSM.add_transition(&stateNB, &stateON, EVENT_RECEIVED_MSG, NULL, "Received text");
@@ -372,7 +370,7 @@ void PowerFSM_setup()
// Don't add power saving transitions if we are a power saving tracker or sensor or have Wifi enabled. Sleep will be initiated
// through the modules
#if HAS_WIFI || !defined(MESHTASTIC_EXCLUDE_WIFI)
#if HAS_WIFI && !defined(MESHTASTIC_EXCLUDE_WIFI)
bool isTrackerOrSensor = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR;

View File

@@ -2,6 +2,12 @@
#include "configuration.h"
#ifdef PowerFSMDebug
#define LOG_POWERFSM(...) LOG_DEBUG(__VA_ARGS__)
#else
#define LOG_POWERFSM(...)
#endif
// See sw-design.md for documentation
#define EVENT_PRESS 1

View File

@@ -50,6 +50,7 @@ void consolePrintf(const char *format, ...)
SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), concurrency::OSThread("SerialConsole")
{
api_type = TYPE_SERIAL;
assert(!console);
console = this;
canWrite = false; // We don't send packets to our port until it has talked to us first

View File

@@ -16,6 +16,7 @@ struct ToneDuration {
};
// Some common frequencies.
#define NOTE_SILENT 1
#define NOTE_C3 131
#define NOTE_CS3 139
#define NOTE_D3 147
@@ -29,11 +30,16 @@ struct ToneDuration {
#define NOTE_AS3 233
#define NOTE_B3 247
#define NOTE_CS4 277
#define NOTE_B4 494
#define NOTE_F5 698
#define NOTE_G6 1568
#define NOTE_E7 2637
const int DURATION_1_16 = 62; // 1/16 note
const int DURATION_1_8 = 125; // 1/8 note
const int DURATION_1_4 = 250; // 1/4 note
const int DURATION_1_2 = 500; // 1/2 note
const int DURATION_3_4 = 750; // 1/4 note
const int DURATION_3_4 = 750; // 3/4 note
const int DURATION_1_1 = 1000; // 1/1 note
void playTones(const ToneDuration *tone_durations, int size)
@@ -71,13 +77,24 @@ void playLongBeep()
void playGPSEnableBeep()
{
#if defined(R1_NEO) || defined(MUZI_BASE)
ToneDuration melody[] = {
{NOTE_F5, DURATION_1_2}, {NOTE_G6, DURATION_1_8}, {NOTE_E7, DURATION_1_4}, {NOTE_SILENT, DURATION_1_2}};
#else
ToneDuration melody[] = {{NOTE_C3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_CS4, DURATION_1_4}};
#endif
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
}
void playGPSDisableBeep()
{
#if defined(R1_NEO) || defined(MUZI_BASE)
ToneDuration melody[] = {{NOTE_B4, DURATION_1_16}, {NOTE_B4, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8},
{NOTE_F3, DURATION_1_16}, {NOTE_F3, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8},
{NOTE_C3, DURATION_1_1}, {NOTE_SILENT, DURATION_1_1}};
#else
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_C3, DURATION_1_4}};
#endif
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
}

View File

@@ -33,6 +33,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "pcf8563.h"
#endif
/* Offer chance for variant-specific defines */
#include "variant.h"
// -----------------------------------------------------------------------------
// Display feature overrides
// -----------------------------------------------------------------------------
// Allow build environments to opt-in explicitly to the E-Ink UI stack while
// keeping headless targets slim by default. Existing variants that already
// define USE_EINK continue to work without additional flags.
#ifndef MESHTASTIC_USE_EINK_UI
#ifdef USE_EINK
#define MESHTASTIC_USE_EINK_UI 1
#else
#define MESHTASTIC_USE_EINK_UI 0
#endif
#endif
#if MESHTASTIC_USE_EINK_UI
#ifndef USE_EINK
#define USE_EINK
#endif
#else
#undef USE_EINK
#endif
// -----------------------------------------------------------------------------
// Version
// -----------------------------------------------------------------------------
@@ -123,6 +149,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define TX_GAIN_LORA 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 9, 9, 8, 7
#endif
#ifdef RAK13302
#define NUM_PA_POINTS 22
#define TX_GAIN_LORA 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8
#endif
// Default system gain to 0 if not defined
#ifndef TX_GAIN_LORA
#define TX_GAIN_LORA 0
@@ -220,6 +251,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define ICM20948_ADDR_ALT 0x68
#define BHI260AP_ADDR 0x28
#define BMM150_ADDR 0x13
#define DA217_ADDR 0x26
// -----------------------------------------------------------------------------
// LED
@@ -241,7 +273,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Touchscreen
// -----------------------------------------------------------------------------
#define FT6336U_ADDR 0x48
#define CST328_ADDR 0x1A
#define CST328_ADDR 0x1A // same address as CST226SE
#define CHSC6X_ADDR 0x2E
#define CST226SE_ADDR_ALT 0x5A
// -----------------------------------------------------------------------------
// RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
@@ -260,9 +294,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// convert 24-bit color to 16-bit (56K)
#define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
/* Step #1: offer chance for variant-specific defines */
#include "variant.h"
#if defined(VEXT_ENABLE) && !defined(VEXT_ON_VALUE)
// Older variant.h files might not be defining this value, so stay with the old default
#define VEXT_ON_VALUE LOW
@@ -363,6 +394,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef HAS_BLUETOOTH
#define HAS_BLUETOOTH 0
#endif
#ifndef USE_TFTDISPLAY
#define USE_TFTDISPLAY 0
#endif
#ifndef HW_VENDOR
#error HW_VENDOR must be defined
@@ -389,6 +423,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define HAS_RGB_LED
#endif
#ifndef LED_STATE_OFF
#define LED_STATE_OFF 0
#endif
#ifndef LED_STATE_ON
#define LED_STATE_ON 1
#endif
// default mapping of pins
#if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN)
#define ALT_BUTTON_PIN PIN_BUTTON2

View File

@@ -82,7 +82,11 @@ class ScanI2C
BHI260AP,
BMM150,
TSL2561,
DRV2605
DRV2605,
BH1750,
DA217,
CHSC6X,
CST226SE
} DeviceType;
// typedef uint8_t DeviceAddress;

View File

@@ -0,0 +1,16 @@
#include "ScanI2CConsumer.h"
#include <forward_list>
static std::forward_list<ScanI2CConsumer *> ScanI2CConsumers;
ScanI2CConsumer::ScanI2CConsumer()
{
ScanI2CConsumers.push_front(this);
}
void ScanI2CCompleted(ScanI2C *i2cScanner)
{
for (ScanI2CConsumer *consumer : ScanI2CConsumers) {
consumer->i2cScanFinished(i2cScanner);
}
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "ScanI2C.h"
#include <stddef.h>
class ScanI2CConsumer
{
public:
ScanI2CConsumer();
virtual void i2cScanFinished(ScanI2C *i2cScanner) = 0;
};
void ScanI2CCompleted(ScanI2C *i2cScanner);

View File

@@ -106,6 +106,7 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation
if (i2cBus->available())
i2cBus->read();
}
LOG_DEBUG("Register value: 0x%x", value);
return value;
}
@@ -377,13 +378,13 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
}
case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT
case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0xe9c || registerValue == 0xc8d) {
type = SHT4X;
logFoundDevice("SHT4X", (uint8_t)addr.address);
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2);
if (registerValue == 0x5449) {
type = OPT3001;
logFoundDevice("OPT3001", (uint8_t)addr.address);
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2) != 0) { // unique SHT4x serial number
type = SHT4X;
logFoundDevice("SHT4X", (uint8_t)addr.address);
} else {
type = SHT31;
logFoundDevice("SHT31", (uint8_t)addr.address);
@@ -464,8 +465,23 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
break;
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address);
case TCA9555_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x01), 1);
if (registerValue == 0x13) {
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
if (registerValue == 0x81) {
type = DA217;
logFoundDevice("DA217", (uint8_t)addr.address);
} else {
type = TCA9555;
logFoundDevice("TCA9555", (uint8_t)addr.address);
}
} else {
type = TCA9555;
logFoundDevice("TCA9555", (uint8_t)addr.address);
}
break;
case TSL25911_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x12), 1);
if (registerValue == 0x50) {
@@ -483,8 +499,38 @@ 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);
case CST328_ADDR:
// Do we have the CST328 or the CST226SE
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xAB), 1);
if (registerValue == 0xA9) {
type = CST226SE;
logFoundDevice("CST226SE", (uint8_t)addr.address);
} else {
type = CST328;
logFoundDevice("CST328", (uint8_t)addr.address);
}
break;
SCAN_SIMPLE_CASE(CHSC6X_ADDR, CHSC6X, "CHSC6X", (uint8_t)addr.address);
case LTR553ALS_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x86), 1); // Part ID register
if (registerValue == 0x92) { // LTR553ALS Part ID
type = LTR553ALS;
logFoundDevice("LTR553ALS", (uint8_t)addr.address);
} else {
// Test BH1750 - send power on command
i2cBus->beginTransmission(addr.address);
i2cBus->write(0x01); // Power On command
uint8_t bh1750_error = i2cBus->endTransmission();
if (bh1750_error == 0) {
type = BH1750;
logFoundDevice("BH1750", (uint8_t)addr.address);
} else {
LOG_INFO("Device found at address 0x%x was not able to be enumerated", (uint8_t)addr.address);
}
}
break;
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);
@@ -493,8 +539,12 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
#endif
case MLX90614_ADDR_DEF:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1);
if (registerValue == 0x5a) {
// Do we have the MLX90614 or the MPR121KB or the CST226SE
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x06), 1);
if (registerValue == 0xAB) {
type = CST226SE;
logFoundDevice("CST226SE", (uint8_t)addr.address);
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1) == 0x5a) {
type = MLX90614;
logFoundDevice("MLX90614", (uint8_t)addr.address);
} else {
@@ -512,6 +562,11 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
case ICM20948_ADDR: // same as BMX160_ADDR
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
#ifdef HAS_ICM20948
type = ICM20948;
logFoundDevice("ICM20948", (uint8_t)addr.address);
break;
#endif
if (registerValue == 0xEA) {
type = ICM20948;
logFoundDevice("ICM20948", (uint8_t)addr.address);
@@ -580,7 +635,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
scanPort(port, nullptr, 0);
}
TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const
TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address)
{
if (address.port == ScanI2C::I2CPort::WIRE) {
return &Wire;

View File

@@ -23,12 +23,12 @@ class ScanI2CTwoWire : public ScanI2C
ScanI2C::FoundDevice find(ScanI2C::DeviceType) const override;
TwoWire *fetchI2CBus(ScanI2C::DeviceAddress) const;
bool exists(ScanI2C::DeviceType) const override;
size_t countDevices() const override;
static TwoWire *fetchI2CBus(ScanI2C::DeviceAddress);
protected:
FoundDevice firstOfOrNONE(size_t, DeviceType[]) const override;

View File

@@ -38,14 +38,16 @@ 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) || defined(ARCH_STM32WL)
#if defined(GPS_SERIAL_PORT)
HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT;
#else
HardwareSerial *GPS::_serial_gps = &Serial1;
#ifndef GPS_SERIAL_PORT
#define GPS_SERIAL_PORT Serial1
#endif
#if defined(ARCH_NRF52)
Uart *GPS::_serial_gps = &GPS_SERIAL_PORT;
#elif defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT;
#elif defined(ARCH_RP2040)
SerialUART *GPS::_serial_gps = &Serial1;
SerialUART *GPS::_serial_gps = &GPS_SERIAL_PORT;
#else
HardwareSerial *GPS::_serial_gps = nullptr;
#endif
@@ -240,6 +242,9 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
buffer[bytesRead] = b;
bytesRead++;
if ((bytesRead == 767) || (b == '\r')) {
#ifdef GPS_DEBUG
LOG_DEBUG(debugmsg.c_str());
#endif
if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
#ifdef GPS_DEBUG
LOG_DEBUG("Found: %s", message); // Log the found message
@@ -247,9 +252,6 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
return GNSS_RESPONSE_OK;
} else {
bytesRead = 0;
#ifdef GPS_DEBUG
LOG_DEBUG(debugmsg.c_str());
#endif
}
}
}
@@ -494,22 +496,10 @@ bool GPS::setup()
if (!didSerialInit) {
int msglen = 0;
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
#ifdef TRACKER_T1000_E
// add power up/down strategy, improve ag3335 detection success
digitalWrite(PIN_GPS_EN, LOW);
delay(500);
digitalWrite(GPS_VRTC_EN, LOW);
delay(1000);
digitalWrite(GPS_VRTC_EN, HIGH);
delay(500);
digitalWrite(PIN_GPS_EN, HIGH);
delay(1000);
#endif
if (probeTries < GPS_PROBETRIES) {
LOG_DEBUG("Probe for GPS at %d", serialSpeeds[speedSelect]);
gnssModel = probe(serialSpeeds[speedSelect]);
if (gnssModel == GNSS_MODEL_UNKNOWN) {
if (++speedSelect == array_count(serialSpeeds)) {
if (currentStep == 0 && ++speedSelect == array_count(serialSpeeds)) {
speedSelect = 0;
++probeTries;
}
@@ -518,10 +508,9 @@ bool GPS::setup()
// Rare Serial Speeds
#ifndef CONFIG_IDF_TARGET_ESP32C6
if (probeTries == GPS_PROBETRIES) {
LOG_DEBUG("Probe for GPS at %d", rareSerialSpeeds[speedSelect]);
gnssModel = probe(rareSerialSpeeds[speedSelect]);
if (gnssModel == GNSS_MODEL_UNKNOWN) {
if (++speedSelect == array_count(rareSerialSpeeds)) {
if (currentStep == 0 && ++speedSelect == array_count(rareSerialSpeeds)) {
LOG_WARN("Give up on GPS probe and set to %d", GPS_BAUDRATE);
return true;
}
@@ -1033,7 +1022,7 @@ void GPS::down()
LOG_DEBUG("%us until next search", sleepTime / 1000);
// If update interval less than 10 seconds, no attempt to sleep
if (updateInterval <= 10 * 1000UL || sleepTime == 0)
if (updateInterval <= GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS || sleepTime == 0)
setPowerState(GPS_IDLE);
else {
@@ -1094,7 +1083,7 @@ int32_t GPS::runOnce()
return disable();
}
if (!setup())
return 2000; // Setup failed, re-run in two seconds
return currentDelay; // Setup failed, re-run in two seconds
// We have now loaded our saved preferences from flash
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
@@ -1104,6 +1093,29 @@ int32_t GPS::runOnce()
publishUpdate();
}
// ======================== GPS_ACTIVE state ========================
// In GPS_ACTIVE state, GPS is powered on and we're receiving NMEA messages.
// We use the following logic to determine when to update the local position
// or time by running GPS::publishUpdate.
// Note: Local position update is asynchronous to position broadcast. We
// generally run this state every gps_update_interval seconds, and in most cases
// gps_update_interval is faster than the position broadcast interval so there's a
// fresh position ready when the device wants to broadcast one on the mesh.
//
// 1. Got a time for the first time --> set the time, don't publish.
// 2. Got a lock for the first time
// --> If gps_update_interval is <= 10s --> publishUpdate
// --> Otherwise, hold for MIN(gps_update_interval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS, 20s)
// 3. Got a lock after turning back on
// --> If gps_update_interval is <= 10s --> publishUpdate
// --> Otherwise, hold for MIN(gps_update_interval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS, 20s)
// 4. Hold has expired
// --> If we have a time and a location --> publishUpdate
// --> down()
// 5. Search time has expired
// --> If we have a time and a location --> publishUpdate
// --> If we had a location before but don't now --> publishUpdate
// --> down()
if (whileActive()) {
// if we have received valid NMEA claim we are connected
setConnected();
@@ -1113,55 +1125,81 @@ int32_t GPS::runOnce()
if (!config.position.fixed_position && powerState != GPS_ACTIVE && scheduling.isUpdateDue())
up();
// If we've already set time from the GPS, no need to ask the GPS
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
gotTime = true;
shouldPublish = true;
}
// quality of the previous fix. We set it to 0 when we go down, so it's a way
// to check if we're getting a lock after being GPS_OFF.
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
}
if (powerState == GPS_ACTIVE) {
// if gps_update_interval is <=10s, GPS never goes off, so we treat that differently
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval);
bool tooLong = scheduling.searchedTooLong();
if (tooLong)
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time");
// 1. Got a time for the first time
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
gotTime = true;
}
// Once we get a location we no longer desperately want an update
if ((gotLoc && gotTime) || tooLong) {
// 2. Got a lock for the first time, or 3. Got a lock after turning back on
bool gotLoc = lookForLocation();
if (gotLoc) {
#ifdef GPS_DEBUG
if (!hasValidLocation) { // declare that we have location ASAP
LOG_DEBUG("hasValidLocation RISING EDGE");
}
#endif
if (updateInterval <= GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS) {
hasValidLocation = true;
shouldPublish = true;
} else if (!hasValidLocation || prev_fixQual == 0 || (fixHoldEnds + GPS_THREAD_INTERVAL) < millis()) {
hasValidLocation = true;
// Hold for up to 20secs after getting a lock to download ephemeris etc
uint32_t holdTime = updateInterval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS;
if (holdTime > GPS_FIX_HOLD_MAX_MS)
holdTime = GPS_FIX_HOLD_MAX_MS;
fixHoldEnds = millis() + holdTime;
#ifdef GPS_DEBUG
LOG_DEBUG("Holding for %ums after lock", holdTime);
#endif
}
}
bool tooLong = scheduling.searchedTooLong();
if (tooLong && !gotLoc) {
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time");
// we didn't get a location during this ack window, therefore declare loss of lock
if (hasValidLocation) {
LOG_DEBUG("hasValidLocation FALLING EDGE");
}
p = meshtastic_Position_init_default;
hasValidLocation = false;
}
if (millis() > fixHoldEnds) {
shouldPublish = true; // publish our update at the end of the lock hold
publishUpdate();
down();
p = meshtastic_Position_init_default;
hasValidLocation = false;
shouldPublish = true;
#ifdef GPS_DEBUG
} else {
LOG_DEBUG("hasValidLocation FALLING EDGE");
#endif
}
}
// Hold has expired , Search time has expired, we got a time only, or we never needed to hold.
bool holdExpired = (fixHoldEnds != 0 && millis() > fixHoldEnds);
if (shouldPublish || tooLong || holdExpired) {
if (gotTime && hasValidLocation) {
shouldPublish = true;
}
if (shouldPublish) {
fixHoldEnds = 0;
publishUpdate();
}
// There's a chance we just got a time, so keep going to see if we can get a location too
if (tooLong || holdExpired) {
down();
}
#ifdef GPS_DEBUG
} else if (fixHoldEnds != 0) {
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
publishUpdate();
// ===================== end GPS_ACTIVE state ========================
if (config.position.fixed_position == true && hasValidLocation)
return disable(); // This should trigger when we have a fixed position, and get that first position
@@ -1218,163 +1256,215 @@ static const char *DETECTED_MESSAGE = "%s detected";
GnssModel_t GPS::probe(int serialSpeed)
{
uint8_t buffer[768] = {0};
switch (currentStep) {
case 0: {
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
_serial_gps->end();
_serial_gps->begin(serialSpeed);
_serial_gps->end();
_serial_gps->begin(serialSpeed);
#elif defined(ARCH_RP2040)
_serial_gps->end();
_serial_gps->setFIFOSize(256);
_serial_gps->begin(serialSpeed);
_serial_gps->end();
_serial_gps->setFIFOSize(256);
_serial_gps->begin(serialSpeed);
#else
if (_serial_gps->baudRate() != serialSpeed) {
LOG_DEBUG("Set Baud to %i", serialSpeed);
_serial_gps->updateBaudRate(serialSpeed);
}
if (_serial_gps->baudRate() != serialSpeed) {
LOG_DEBUG("Set GPS Baud to %i", serialSpeed);
_serial_gps->updateBaudRate(serialSpeed);
}
#endif
memset(&ublox_info, 0, sizeof(ublox_info));
uint8_t buffer[768] = {0};
delay(100);
memset(&ublox_info, 0, sizeof(ublox_info));
delay(100);
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20);
// Close NMEA sequences on Ublox
_serial_gps->write("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n");
_serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n");
_serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n");
delay(20);
// Close NMEA sequences on CM121
_serial_gps->write("$CFGMSG,0,1,0,1*1B\r\n");
_serial_gps->write("$CFGMSG,0,2,0,1*18\r\n");
_serial_gps->write("$CFGMSG,0,3,0,1*19\r\n");
delay(20);
#if defined(PIN_GPS_RESET) && PIN_GPS_RESET != -1
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
delay(10);
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121
std::vector<ChipInfo> unicore = {
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}};
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
std::vector<ChipInfo> atgm = {
{"ATGM336H", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H},
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */
{"ATGM332D", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H}};
PROBE_FAMILY("ATGM33xx Family", "$PCAS06,1*1A", atgm, 500);
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
_serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume
_serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume
_serial_gps->write("$PAIR513*3D\r\n"); // save configuration
std::vector<ChipInfo> airoha = {{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352}};
PROBE_FAMILY("Airoha Family", "$PAIR021*39", airoha, 1000);
PROBE_SIMPLE("LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GNSS_MODEL_AG3352, 500);
PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500);
// Close all NMEA sentences, valid for MTK3333 and MTK3339 platforms
_serial_gps->write("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n");
delay(20);
std::vector<ChipInfo> mtk = {{"L76B", "Quectel-L76B", GNSS_MODEL_MTK_L76B}, {"PA1010D", "1010D", GNSS_MODEL_MTK_PA1010D},
{"PA1616S", "1616S", GNSS_MODEL_MTK_PA1616S}, {"LS20031", "MC-1513", GNSS_MODEL_MTK_L76B},
{"L96", "Quectel-L96", GNSS_MODEL_MTK_L76B}, {"L80-R", "_3337_", GNSS_MODEL_MTK_L76B},
{"L80", "_3339_", GNSS_MODEL_MTK_L76B}};
PROBE_FAMILY("MTK Family", "$PMTK605*31", mtk, 500);
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
UBXChecksum(cfg_rate, sizeof(cfg_rate));
clearBuffer();
_serial_gps->write(cfg_rate, sizeof(cfg_rate));
// Check that the returned response class and message ID are correct
GPS_RESPONSE response = getACK(0x06, 0x08, 750);
if (response == GNSS_RESPONSE_NONE) {
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
// attempt to detect the chip based on boot messages
std::vector<ChipInfo> passive_detect = {
{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352},
{"UC6580", "UC6580", GNSS_MODEL_UC6580},
// as L76K is sort of a last ditch effort, we won't attempt to detect it by startup messages for now.
/*{"L76K", "SW=URANUS", GNSS_MODEL_MTK}*/};
GnssModel_t detectedDriver = getProbeResponse(500, passive_detect, serialSpeed);
if (detectedDriver != GNSS_MODEL_UNKNOWN) {
return detectedDriver;
}
#endif
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20);
// Close NMEA sequences on Ublox
_serial_gps->write("$PUBX,40,GLL,0,0,0,0,0,0*5C\r\n");
_serial_gps->write("$PUBX,40,GSV,0,0,0,0,0,0*59\r\n");
_serial_gps->write("$PUBX,40,VTG,0,0,0,0,0,0*5E\r\n");
delay(20);
// Close NMEA sequences on CM121
_serial_gps->write("$CFGMSG,0,1,0,1*1B\r\n");
_serial_gps->write("$CFGMSG,0,2,0,1*18\r\n");
_serial_gps->write("$CFGMSG,0,3,0,1*19\r\n");
currentDelay = 20;
currentStep = 1;
return GNSS_MODEL_UNKNOWN;
} else if (response == GNSS_RESPONSE_FRAME_ERRORS) {
LOG_INFO("UBlox Frame Errors (baudrate %d)", serialSpeed);
}
case 1: {
memset(buffer, 0, sizeof(buffer));
uint8_t _message_MONVER[8] = {
0xB5, 0x62, // Sync message for UBX protocol
0x0A, 0x04, // Message class and ID (UBX-MON-VER)
0x00, 0x00, // Length of payload (we're asking for an answer, so no payload)
0x00, 0x00 // Checksum
};
// Get Ublox gnss module hardware and software info
UBXChecksum(_message_MONVER, sizeof(_message_MONVER));
clearBuffer();
_serial_gps->write(_message_MONVER, sizeof(_message_MONVER));
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121
std::vector<ChipInfo> unicore = {
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}};
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
currentDelay = 20;
currentStep = 2;
return GNSS_MODEL_UNKNOWN;
}
case 2: {
std::vector<ChipInfo> atgm = {
{"ATGM336H", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H},
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */
{"ATGM332D", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H}};
PROBE_FAMILY("ATGM33xx Family", "$PCAS06,1*1A", atgm, 500);
currentDelay = 20;
currentStep = 3;
return GNSS_MODEL_UNKNOWN;
}
case 3: {
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
_serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume
_serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume
_serial_gps->write("$PAIR513*3D\r\n"); // save configuration
std::vector<ChipInfo> airoha = {{"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352}};
PROBE_FAMILY("Airoha Family", "$PAIR021*39", airoha, 1000);
currentDelay = 20;
currentStep = 4;
return GNSS_MODEL_UNKNOWN;
}
case 4: {
PROBE_SIMPLE("LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GNSS_MODEL_AG3352, 500);
PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500);
currentDelay = 20;
currentStep = 5;
return GNSS_MODEL_UNKNOWN;
}
case 5: {
uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200);
if (len) {
uint16_t position = 0;
for (int i = 0; i < 30; i++) {
ublox_info.swVersion[i] = buffer[position];
position++;
}
for (int i = 0; i < 10; i++) {
ublox_info.hwVersion[i] = buffer[position];
position++;
}
// Close all NMEA sentences, valid for MTK3333 and MTK3339 platforms
_serial_gps->write("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n");
delay(20);
std::vector<ChipInfo> mtk = {{"L76B", "Quectel-L76B", GNSS_MODEL_MTK_L76B}, {"PA1010D", "1010D", GNSS_MODEL_MTK_PA1010D},
{"PA1616S", "1616S", GNSS_MODEL_MTK_PA1616S}, {"LS20031", "MC-1513", GNSS_MODEL_MTK_L76B},
{"L96", "Quectel-L96", GNSS_MODEL_MTK_L76B}, {"L80-R", "_3337_", GNSS_MODEL_MTK_L76B},
{"L80", "_3339_", GNSS_MODEL_MTK_L76B}};
while (len >= position + 30) {
for (int i = 0; i < 30; i++) {
ublox_info.extension[ublox_info.extensionNo][i] = buffer[position];
position++;
}
ublox_info.extensionNo++;
if (ublox_info.extensionNo > 9)
break;
}
LOG_DEBUG("Module Info : ");
LOG_DEBUG("Soft version: %s", ublox_info.swVersion);
LOG_DEBUG("Hard version: %s", ublox_info.hwVersion);
LOG_DEBUG("Extensions:%d", ublox_info.extensionNo);
for (int i = 0; i < ublox_info.extensionNo; i++) {
LOG_DEBUG(" %s", ublox_info.extension[i]);
PROBE_FAMILY("MTK Family", "$PMTK605*31", mtk, 500);
currentDelay = 20;
currentStep = 6;
return GNSS_MODEL_UNKNOWN;
}
case 6: {
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
UBXChecksum(cfg_rate, sizeof(cfg_rate));
clearBuffer();
_serial_gps->write(cfg_rate, sizeof(cfg_rate));
// Check that the returned response class and message ID are correct
GPS_RESPONSE response = getACK(0x06, 0x08, 750);
if (response == GNSS_RESPONSE_NONE) {
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
currentDelay = 2000;
currentStep = 0;
return GNSS_MODEL_UNKNOWN;
} else if (response == GNSS_RESPONSE_FRAME_ERRORS) {
LOG_INFO("UBlox Frame Errors (baudrate %d)", serialSpeed);
}
memset(buffer, 0, sizeof(buffer));
uint8_t _message_MONVER[8] = {
0xB5, 0x62, // Sync message for UBX protocol
0x0A, 0x04, // Message class and ID (UBX-MON-VER)
0x00, 0x00, // Length of payload (we're asking for an answer, so no payload)
0x00, 0x00 // Checksum
};
// Get Ublox gnss module hardware and software info
UBXChecksum(_message_MONVER, sizeof(_message_MONVER));
clearBuffer();
_serial_gps->write(_message_MONVER, sizeof(_message_MONVER));
// tips: extensionNo field is 0 on some 6M GNSS modules
for (int i = 0; i < ublox_info.extensionNo; ++i) {
if (!strncmp(ublox_info.extension[i], "MOD=", 4)) {
strncpy((char *)buffer, &(ublox_info.extension[i][4]), sizeof(buffer));
} else if (!strncmp(ublox_info.extension[i], "PROTVER", 7)) {
char *ptr = nullptr;
memset(buffer, 0, sizeof(buffer));
strncpy((char *)buffer, &(ublox_info.extension[i][8]), sizeof(buffer));
LOG_DEBUG("Protocol Version:%s", (char *)buffer);
if (strlen((char *)buffer)) {
ublox_info.protocol_version = strtoul((char *)buffer, &ptr, 10);
LOG_DEBUG("ProtVer=%d", ublox_info.protocol_version);
} else {
ublox_info.protocol_version = 0;
uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200);
if (len) {
uint16_t position = 0;
for (int i = 0; i < 30; i++) {
ublox_info.swVersion[i] = buffer[position];
position++;
}
for (int i = 0; i < 10; i++) {
ublox_info.hwVersion[i] = buffer[position];
position++;
}
while (len >= position + 30) {
for (int i = 0; i < 30; i++) {
ublox_info.extension[ublox_info.extensionNo][i] = buffer[position];
position++;
}
ublox_info.extensionNo++;
if (ublox_info.extensionNo > 9)
break;
}
LOG_DEBUG("Module Info : ");
LOG_DEBUG("Soft version: %s", ublox_info.swVersion);
LOG_DEBUG("Hard version: %s", ublox_info.hwVersion);
LOG_DEBUG("Extensions:%d", ublox_info.extensionNo);
for (int i = 0; i < ublox_info.extensionNo; i++) {
LOG_DEBUG(" %s", ublox_info.extension[i]);
}
memset(buffer, 0, sizeof(buffer));
// tips: extensionNo field is 0 on some 6M GNSS modules
for (int i = 0; i < ublox_info.extensionNo; ++i) {
if (!strncmp(ublox_info.extension[i], "MOD=", 4)) {
strncpy((char *)buffer, &(ublox_info.extension[i][4]), sizeof(buffer));
} else if (!strncmp(ublox_info.extension[i], "PROTVER", 7)) {
char *ptr = nullptr;
memset(buffer, 0, sizeof(buffer));
strncpy((char *)buffer, &(ublox_info.extension[i][8]), sizeof(buffer));
LOG_DEBUG("Protocol Version:%s", (char *)buffer);
if (strlen((char *)buffer)) {
ublox_info.protocol_version = strtoul((char *)buffer, &ptr, 10);
LOG_DEBUG("ProtVer=%d", ublox_info.protocol_version);
} else {
ublox_info.protocol_version = 0;
}
}
}
}
if (strncmp(ublox_info.hwVersion, "00040007", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6");
return GNSS_MODEL_UBLOX6;
} else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7");
return GNSS_MODEL_UBLOX7;
} else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8");
return GNSS_MODEL_UBLOX8;
} else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9");
return GNSS_MODEL_UBLOX9;
} else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10");
return GNSS_MODEL_UBLOX10;
if (strncmp(ublox_info.hwVersion, "00040007", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 6", "6");
return GNSS_MODEL_UBLOX6;
} else if (strncmp(ublox_info.hwVersion, "00070000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 7", "7");
return GNSS_MODEL_UBLOX7;
} else if (strncmp(ublox_info.hwVersion, "00080000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 8", "8");
return GNSS_MODEL_UBLOX8;
} else if (strncmp(ublox_info.hwVersion, "00190000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 9", "9");
return GNSS_MODEL_UBLOX9;
} else if (strncmp(ublox_info.hwVersion, "000A0000", 8) == 0) {
LOG_INFO(DETECTED_MESSAGE, "U-blox 10", "10");
return GNSS_MODEL_UBLOX10;
}
}
}
}
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
currentDelay = 2000;
currentStep = 0;
return GNSS_MODEL_UNKNOWN;
}
@@ -1403,12 +1493,12 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
}
if (c == ',' || (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n')) {
#ifdef GPS_DEBUG
LOG_DEBUG(response);
#endif
// check if we can see our chips
for (const auto &chipInfo : responseMap) {
if (strstr(response, chipInfo.detectionString.c_str()) != nullptr) {
#ifdef GPS_DEBUG
LOG_DEBUG(response);
#endif
LOG_INFO("%s detected", chipInfo.chipName.c_str());
delete[] response; // Cleanup before return
return chipInfo.driver;
@@ -1416,6 +1506,9 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector<ChipI
}
}
if (responseLen >= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n') {
#ifdef GPS_DEBUG
LOG_DEBUG(response);
#endif
// Reset the response buffer for the next potential message
responseLen = 0;
response[0] = '\0';
@@ -1434,10 +1527,7 @@ GPS *GPS::createGps()
int8_t _rx_gpio = config.position.rx_gpio;
int8_t _tx_gpio = config.position.tx_gpio;
int8_t _en_gpio = config.position.gps_en_gpio;
#if HAS_GPS && !defined(ARCH_ESP32)
_rx_gpio = 1; // We only specify GPS serial ports on ESP32. Otherwise, these are just flags.
_tx_gpio = 1;
#endif
#if defined(GPS_RX_PIN)
if (!_rx_gpio)
_rx_gpio = GPS_RX_PIN;
@@ -1502,8 +1592,6 @@ GPS *GPS::createGps()
#ifdef PIN_GPS_RESET
pinMode(PIN_GPS_RESET, OUTPUT);
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
delay(10);
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
#endif
@@ -1513,16 +1601,28 @@ GPS *GPS::createGps()
_serial_gps->setRxBufferSize(SERIAL_BUFFER_SIZE); // the default is 256
#endif
// ESP32 has a special set of parameters vs other arduino ports
#if defined(ARCH_ESP32)
LOG_DEBUG("Use GPIO%d for GPS RX", new_gps->rx_gpio);
LOG_DEBUG("Use GPIO%d for GPS TX", new_gps->tx_gpio);
// ESP32 has a special set of parameters vs other arduino ports
#if defined(ARCH_ESP32)
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, new_gps->rx_gpio, new_gps->tx_gpio);
#elif defined(ARCH_RP2040)
_serial_gps->setPinout(new_gps->tx_gpio, new_gps->rx_gpio);
_serial_gps->setFIFOSize(256);
_serial_gps->begin(GPS_BAUDRATE);
#else
#elif defined(ARCH_NRF52)
_serial_gps->setPins(new_gps->rx_gpio, new_gps->tx_gpio);
_serial_gps->begin(GPS_BAUDRATE);
#elif defined(ARCH_STM32WL)
_serial_gps->setTx(new_gps->tx_gpio);
_serial_gps->setRx(new_gps->rx_gpio);
_serial_gps->begin(GPS_BAUDRATE);
#elif defined(ARCH_PORTDUINO)
// Portduino can't set the GPS pins directly.
_serial_gps->begin(GPS_BAUDRATE);
#else
#error Unsupported architecture!
#endif
}
return new_gps;
@@ -1589,8 +1689,12 @@ bool GPS::lookForLocation()
#ifndef TINYGPS_OPTION_NO_STATISTICS
if (reader.failedChecksum() > lastChecksumFailCount) {
LOG_WARN("%u new GPS checksum failures, for a total of %u", reader.failedChecksum() - lastChecksumFailCount,
reader.failedChecksum());
// In a GPS_DEBUG build we want to log all of these. In production, we only care if there are many of them.
#ifndef GPS_DEBUG
if (reader.failedChecksum() > 4)
#endif
LOG_WARN("%u new GPS checksum failures, for a total of %u", reader.failedChecksum() - lastChecksumFailCount,
reader.failedChecksum());
lastChecksumFailCount = reader.failedChecksum();
}
#endif

View File

@@ -16,6 +16,9 @@
#define GPS_EN_ACTIVE 1
#endif
static constexpr uint32_t GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS = 10 * 1000UL;
static constexpr uint32_t GPS_FIX_HOLD_MAX_MS = 20000;
typedef enum {
GNSS_MODEL_ATGM336H,
GNSS_MODEL_MTK,
@@ -151,6 +154,8 @@ class GPS : private concurrency::OSThread
TinyGPSPlus reader;
uint8_t fixQual = 0; // fix quality from GPGGA
uint32_t lastChecksumFailCount = 0;
uint8_t currentStep = 0;
int32_t currentDelay = 2000;
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
@@ -173,8 +178,6 @@ class GPS : private concurrency::OSThread
*/
bool hasValidLocation = false; // default to false, until we complete our first read
bool isInPowersave = false;
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
bool hasGPS = false; // Do we have a GPS we are talking to
@@ -191,6 +194,8 @@ class GPS : private concurrency::OSThread
/** If !NULL we will use this serial port to construct our GPS */
#if defined(ARCH_RP2040)
static SerialUART *_serial_gps;
#elif defined(ARCH_NRF52)
static Uart *_serial_gps;
#else
static HardwareSerial *_serial_gps;
#endif

View File

@@ -112,7 +112,11 @@ RTCSetResult readFromRTC()
#elif defined(RX8130CE_RTC)
if (rtc_found.address == RX8130CE_RTC) {
uint32_t now = millis();
#ifdef MUZI_BASE
ArtronShop_RX8130CE rtc(&Wire1);
#else
ArtronShop_RX8130CE rtc(&Wire);
#endif
tm t;
if (rtc.getTime(&t)) {
tv.tv_sec = gm_mktime(&t);
@@ -245,7 +249,11 @@ RTCSetResult perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpd
}
#elif defined(RX8130CE_RTC)
if (rtc_found.address == RX8130CE_RTC) {
#ifdef MUZI_BASE
ArtronShop_RX8130CE rtc(&Wire1);
#else
ArtronShop_RX8130CE rtc(&Wire);
#endif
tm *t = gmtime(&tv->tv_sec);
if (rtc.setTime(*t)) {
LOG_DEBUG("RX8130CE setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1,
@@ -310,7 +318,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
#ifdef BUILD_EPOCH
if (tv.tv_sec < BUILD_EPOCH) {
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
LOG_WARN("Ignore time (%lu) before build epoch (%lu)!", printableEpoch, BUILD_EPOCH);
lastTimeValidationWarning = millis();
}
return RTCSetResultInvalidTime;
@@ -319,7 +327,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
// Calculate max allowed time safely to avoid overflow in logging
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch,
LOG_WARN("Ignore time (%lu) too far in the future (build epoch: %lu, max allowed: %lu)!", printableEpoch,
(uint32_t)BUILD_EPOCH, maxAllowedPrintable);
lastTimeValidationWarning = millis();
}

View File

@@ -69,7 +69,11 @@ using graphics::Emote;
using graphics::emotes;
using graphics::numEmotes;
#if USE_TFTDISPLAY
extern uint16_t TFT_MESH;
#else
uint16_t TFT_MESH = COLOR565(0x67, 0xEA, 0x94);
#endif
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
#include "mesh/wifi/WiFiAPClient.h"
@@ -226,24 +230,9 @@ void Screen::showTextInput(const char *header, const char *initialText, uint32_t
{
LOG_INFO("showTextInput called with header='%s', durationMs=%d", header ? header : "NULL", durationMs);
if (NotificationRenderer::virtualKeyboard) {
delete NotificationRenderer::virtualKeyboard;
NotificationRenderer::virtualKeyboard = nullptr;
}
NotificationRenderer::textInputCallback = nullptr;
NotificationRenderer::virtualKeyboard = new VirtualKeyboard();
if (header) {
NotificationRenderer::virtualKeyboard->setHeader(header);
}
if (initialText) {
NotificationRenderer::virtualKeyboard->setInputText(initialText);
}
// Set up callback with safer cleanup mechanism
// Start OnScreenKeyboardModule session (non-touch variant)
OnScreenKeyboardModule::instance().start(header, initialText, durationMs, textCallback);
NotificationRenderer::textInputCallback = textCallback;
NotificationRenderer::virtualKeyboard->setCallback([textCallback](const std::string &text) { textCallback(text); });
// Store the message and set the expiration timestamp (use same pattern as other notifications)
strncpy(NotificationRenderer::alertBannerMessage, header ? header : "Text Input", 255);
@@ -324,7 +313,7 @@ static int8_t prevFrame = -1;
// Combined dynamic node list frame cycling through LastHeard, HopSignal, and Distance modes
// Uses a single frame and changes data every few seconds (E-Ink variant is separate)
#if defined(ESP_PLATFORM) && defined(USE_ST7789)
#if defined(ESP_PLATFORM) && (defined(USE_ST7789) || defined(USE_ST7796))
SPIClass SPI1(HSPI);
#endif
@@ -356,7 +345,13 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#else
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
#endif
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
#elif defined(USE_ST7796)
#ifdef ESP_PLATFORM
dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT, ST7796_SDA,
ST7796_MISO, ST7796_SCK, TFT_SPI_FREQUENCY);
#else
dispdev = new ST7796Spi(&SPI1, ST7796_RESET, ST7796_RS, ST7796_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
#endif
#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);
@@ -369,7 +364,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
LOG_INFO("SSD1306 init success");
}
#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(ST7796_CS)
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR)
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)
@@ -399,6 +394,12 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
isAUTOOled = true;
#endif
#if defined(USE_ST7789)
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
#elif defined(USE_ST7796)
static_cast<ST7796Spi *>(dispdev)->setRGB(TFT_MESH);
#endif
ui = new OLEDDisplayUi(dispdev);
cmdQueue.setReader(this);
}
@@ -435,6 +436,14 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
PMU->enablePowerOutput(XPOWERS_ALDO2);
#endif
#if defined(MUZI_BASE)
dispdev->init();
dispdev->setBrightness(brightness);
dispdev->flipScreenVertically();
dispdev->resetDisplay();
digitalWrite(SCREEN_12V_ENABLE, HIGH);
delay(100);
#endif
#if !ARCH_PORTDUINO
dispdev->displayOn();
#endif
@@ -443,7 +452,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
if (uiconfig.screen_brightness == 1)
digitalWrite(PIN_EINK_EN, HIGH);
#elif defined(PCA_PIN_EINK_EN)
if (uiconfig.screen_brightness == 1)
if (uiconfig.screen_brightness > 0)
io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
#endif
@@ -466,6 +475,15 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
#endif
#endif
#ifdef USE_ST7796
ui->init();
#ifdef ESP_PLATFORM
analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT);
#else
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
#endif
#endif
enabled = true;
setInterval(0); // Draw ASAP
@@ -484,6 +502,10 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
#endif
dispdev->displayOff();
#ifdef SCREEN_12V_ENABLE
digitalWrite(SCREEN_12V_ENABLE, LOW);
#endif
#ifdef USE_ST7789
SPI1.end();
#if defined(ARCH_ESP32)
@@ -500,6 +522,21 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
nrf_gpio_cfg_default(ST7789_NSS);
#endif
#endif
#ifdef USE_ST7796
SPI1.end();
#if defined(ARCH_ESP32)
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, LOW);
pinMode(ST7796_RESET, ANALOG);
pinMode(ST7796_RS, ANALOG);
pinMode(ST7796_NSS, ANALOG);
#else
nrf_gpio_cfg_default(VTFT_LEDA);
nrf_gpio_cfg_default(ST7796_RESET);
nrf_gpio_cfg_default(ST7796_RS);
nrf_gpio_cfg_default(ST7796_NSS);
#endif
#endif
#ifdef T_WATCH_S3
PMU->disablePowerOutput(XPOWERS_ALDO2);
@@ -534,7 +571,7 @@ void Screen::setup()
static_cast<AutoOLEDWire *>(dispdev)->setDetected(model);
#endif
#ifdef USE_SH1107_128_64
#if defined(USE_SH1107_128_64) || defined(USE_SH1107)
static_cast<SH1106Wire *>(dispdev)->setSubtype(7);
#endif
@@ -542,6 +579,13 @@ void Screen::setup()
// Apply custom RGB color (e.g. Heltec T114/T190)
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
#endif
#if defined(MUZI_BASE)
dispdev->delayPoweron = true;
#endif
#if defined(USE_ST7796) && defined(TFT_MESH)
// Custom text color, if defined in variant.h
static_cast<ST7796Spi *>(dispdev)->setRGB(TFT_MESH);
#endif
// === Initialize display and UI system ===
ui->init();
@@ -601,10 +645,12 @@ 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(ST7796_CS)
defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(HACKADAY_COMMUNICATOR)
static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
#elif defined(USE_ST7789)
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
#elif defined(USE_ST7796)
static_cast<ST7796Spi *>(dispdev)->mirrorScreen();
#elif !defined(M5STACK_UNITC6L)
dispdev->flipScreenVertically();
#endif
@@ -637,7 +683,7 @@ void Screen::setup()
touchScreenImpl1->init();
}
}
#elif HAS_TOUCHSCREEN && !defined(USE_EINK)
#elif HAS_TOUCHSCREEN && !defined(USE_EINK) && !HAS_CST226SE
touchScreenImpl1 =
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
touchScreenImpl1->init();
@@ -1428,6 +1474,9 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
}
nodeDB->updateGUI = false;
break;
case STATUS_TYPE_POWER:
forceDisplay(true);
break;
}
return 0;
@@ -1449,14 +1498,14 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
// Incoming message
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
setFrames(FOCUS_PRESERVE); // Refresh frame list without switching view (no-op during text_input)
// 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 ===
// === Prepare banner/popup content ===
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
const meshtastic_Channel channel =
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
@@ -1480,38 +1529,84 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
// Unlike generic messages, alerts (when enabled via the ext notif module) ignore any
// 'mute' preferences set to any specific node or channel.
if (isAlert) {
if (longName && longName[0]) {
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
} else {
strcpy(banner, "Alert Received");
// If on-screen keyboard is active, show a transient popup over keyboard instead of interrupting it
if (NotificationRenderer::current_notification_type == notificationTypeEnum::text_input) {
// Wake and force redraw so popup is visible immediately
if (shouldWakeOnReceivedMessage()) {
setOn(true);
forceDisplay();
}
screen->showSimpleBanner(banner, 3000);
} else if (!channel.settings.mute) {
if (longName && longName[0]) {
#if defined(M5STACK_UNITC6L)
strcpy(banner, "New Message");
#else
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
#endif
// Build popup: title = message source name, content = message text (sanitized)
// Title
char titleBuf[64] = {0};
if (longName && longName[0]) {
// Sanitize sender name
std::string t = sanitizeString(longName);
strncpy(titleBuf, t.c_str(), sizeof(titleBuf) - 1);
} else {
strcpy(banner, "New Message");
strncpy(titleBuf, "Message", sizeof(titleBuf) - 1);
}
// Content: payload bytes may not be null-terminated, remove ASCII_BELL and sanitize
char content[256] = {0};
{
std::string raw;
raw.reserve(packet->decoded.payload.size);
for (size_t i = 0; i < packet->decoded.payload.size; ++i) {
char c = msgRaw[i];
if (c == ASCII_BELL)
continue; // strip bell
raw.push_back(c);
}
std::string sanitized = sanitizeString(raw);
strncpy(content, sanitized.c_str(), sizeof(content) - 1);
}
NotificationRenderer::showKeyboardMessagePopupWithTitle(titleBuf, content, 3000);
// Maintain existing buzzer behavior on M5 if applicable
#if defined(M5STACK_UNITC6L)
screen->setOn(true);
screen->showSimpleBanner(banner, 1500);
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
(!isBroadcast(packet->to) && isToUs(p))) {
// Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either
// - packet contains an alert and alert bell buzzer is enabled
// - packet is a non-broadcast that is addressed to this node
(!isBroadcast(packet->to) && isToUs(packet))) {
playLongBeep();
}
#else
screen->showSimpleBanner(banner, 3000);
#endif
} else {
// No keyboard active: use regular banner flow, respecting mute settings
if (isAlert) {
if (longName && longName[0]) {
snprintf(banner, sizeof(banner), "Alert Received from\n%s", longName);
} else {
strcpy(banner, "Alert Received");
}
screen->showSimpleBanner(banner, 3000);
} else if (!channel.settings.has_module_settings || !channel.settings.module_settings.is_muted) {
if (longName && longName[0]) {
#if defined(M5STACK_UNITC6L)
strcpy(banner, "New Message");
#else
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
#endif
} else {
strcpy(banner, "New Message");
}
#if defined(M5STACK_UNITC6L)
screen->setOn(true);
screen->showSimpleBanner(banner, 1500);
if (config.device.buzzer_mode != meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY ||
(isAlert && moduleConfig.external_notification.alert_bell_buzzer) ||
(!isBroadcast(packet->to) && isToUs(packet))) {
// Beep if not in DIRECT_MSG_ONLY mode or if in DIRECT_MSG_ONLY mode and either
// - packet contains an alert and alert bell buzzer is enabled
// - packet is a non-broadcast that is addressed to this node
playLongBeep();
}
#else
screen->showSimpleBanner(banner, 3000);
#endif
}
}
}
}
@@ -1546,6 +1641,7 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
int Screen::handleInputEvent(const InputEvent *event)
{
LOG_INPUT("Screen Input event %u! kb %u", event->inputEvent, event->kbchar);
if (!screenOn)
return 0;
@@ -1593,6 +1689,12 @@ int Screen::handleInputEvent(const InputEvent *event)
showPrevFrame();
} else if (event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS) {
showNextFrame();
} else if (event->inputEvent == INPUT_BROKER_UP_LONG) {
// Long press up button for fast frame switching
showPrevFrame();
} else if (event->inputEvent == INPUT_BROKER_DOWN_LONG) {
// Long press down button for fast frame switching
showNextFrame();
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
menuHandler::homeBaseMenu();

View File

@@ -83,6 +83,8 @@ class Screen
#include <ST7789Spi.h>
#elif defined(USE_SPISSD1306)
#include <SSD1306Spi.h>
#elif defined(USE_ST7796)
#include <ST7796Spi.h>
#else
// the SH1106/SSD1306 variant is auto-detected
#include <AutoOLEDWire.h>
@@ -249,6 +251,8 @@ class Screen : public concurrency::OSThread
bool isOverlayBannerShowing();
bool isScreenOn() { return screenOn; }
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
// FIXME: Needs refactoring and getMacAddr needs to be moved to a utility class
char ourId[5];

View File

@@ -73,7 +73,8 @@
#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(ST7796_CS)) && \
defined(ST7789_CS) || defined(USE_ST7789) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
// The screen is bigger so use bigger fonts
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19

View File

@@ -1,6 +1,10 @@
#include "graphics/SharedUIDisplay.h"
#include "configuration.h"
#if HAS_SCREEN
#include "MeshService.h"
#include "RTC.h"
#include "draw/NodeListRenderer.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
#include "graphics/draw/UIRenderer.h"
#include "main.h"
#include "meshtastic/config.pb.h"
@@ -13,6 +17,12 @@ namespace graphics
void determineResolution(int16_t screenheight, int16_t screenwidth)
{
#ifdef FORCE_LOW_RES
isHighResolution = false;
return;
#endif
if (screenwidth > 128) {
isHighResolution = true;
}
@@ -20,11 +30,6 @@ void determineResolution(int16_t screenheight, int16_t screenwidth)
if (screenwidth > 128 && screenheight <= 64) {
isHighResolution = false;
}
// Special case for Heltec Wireless Tracker v1.1
if (screenwidth == 160 && screenheight == 80) {
isHighResolution = false;
}
}
// === Shared External State ===
@@ -396,6 +401,43 @@ const int *getTextPositions(OLEDDisplay *display)
return textPositions;
}
// *************************
// * Common Footer Drawing *
// *************************
void drawCommonFooter(OLEDDisplay *display, int16_t x, int16_t y)
{
bool drawConnectionState = false;
if (service->api_state == service->STATE_BLE || service->api_state == service->STATE_WIFI ||
service->api_state == service->STATE_SERIAL || service->api_state == service->STATE_PACKET ||
service->api_state == service->STATE_HTTP || service->api_state == service->STATE_ETH) {
drawConnectionState = true;
}
if (drawConnectionState) {
if (isHighResolution) {
const int scale = 2;
const int bytesPerRow = (connection_icon_width + 7) / 8;
int iconX = 0;
int iconY = SCREEN_HEIGHT - (connection_icon_height * 2);
for (int yy = 0; yy < connection_icon_height; ++yy) {
const uint8_t *rowPtr = connection_icon + yy * bytesPerRow;
for (int xx = 0; xx < connection_icon_width; ++xx) {
const uint8_t byteVal = pgm_read_byte(rowPtr + (xx >> 3));
const uint8_t bitMask = 1U << (xx & 7); // XBM is LSB-first
if (byteVal & bitMask) {
display->fillRect(iconX + xx * scale, iconY + yy * scale, scale, scale);
}
}
}
} else {
display->drawXbm(0, SCREEN_HEIGHT - connection_icon_height, connection_icon_width, connection_icon_height,
connection_icon);
}
}
}
bool isAllowedPunctuation(char c)
{
const std::string allowed = ".,!?;:-_()[]{}'\"@#$/\\&+=%~^ ";
@@ -423,3 +465,4 @@ std::string sanitizeString(const std::string &input)
}
} // namespace graphics
#endif

View File

@@ -52,6 +52,9 @@ void drawRoundedHighlight(OLEDDisplay *display, int16_t x, int16_t y, int16_t w,
void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr = "", bool force_no_invert = false,
bool show_date = false);
// Shared battery/time/mail header
void drawCommonFooter(OLEDDisplay *display, int16_t x, int16_t y);
const int *getTextPositions(OLEDDisplay *display);
bool isAllowedPunctuation(char c);

View File

@@ -1,5 +1,6 @@
#include "configuration.h"
#include "main.h"
#if USE_TFTDISPLAY
#if ARCH_PORTDUINO
#include "platform/portduino/PortduinoGlue.h"
@@ -123,6 +124,11 @@ static void rak14014_tpIntHandle(void)
_rak14014_touch_int = true;
}
#elif defined(HACKADAY_COMMUNICATOR)
#include <Arduino_GFX_Library.h>
Arduino_DataBus *bus = nullptr;
Arduino_GFX *tft = nullptr;
#elif defined(ST72xx_DE)
#include <LovyanGFX.hpp>
#include <TCA9534.h>
@@ -422,7 +428,57 @@ static LGFX *tft = nullptr;
#elif defined(ST7789_CS)
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
#ifdef HELTEC_V4_TFT
#include "chsc6x.h"
#include "lgfx/v1/Touch.hpp"
namespace lgfx
{
inline namespace v1
{
class TOUCH_CHSC6X : public ITouch
{
public:
TOUCH_CHSC6X(void)
{
_cfg.i2c_addr = TOUCH_SLAVE_ADDRESS;
_cfg.x_min = 0;
_cfg.x_max = 240;
_cfg.y_min = 0;
_cfg.y_max = 320;
};
bool init(void) override
{
if (chsc6xTouch == nullptr) {
chsc6xTouch = new chsc6x(&Wire1, TOUCH_SDA_PIN, TOUCH_SCL_PIN, TOUCH_INT_PIN, TOUCH_RST_PIN);
}
chsc6xTouch->chsc6x_init();
return true;
};
uint_fast8_t getTouchRaw(touch_point_t *tp, uint_fast8_t count) override
{
uint16_t raw_x, raw_y;
if (chsc6xTouch->chsc6x_read_touch_info(&raw_x, &raw_y) == 0) {
tp[0].x = 320 - 1 - raw_y;
tp[0].y = 240 - 1 - raw_x;
tp[0].size = 1;
tp[0].id = 1;
return 1;
}
tp[0].size = 0;
return 0;
};
void wakeup(void) override{};
void sleep(void) override{};
private:
chsc6x *chsc6xTouch = nullptr;
};
} // namespace v1
} // namespace lgfx
#endif
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_ST7789 _panel_instance;
@@ -431,6 +487,8 @@ class LGFX : public lgfx::LGFX_Device
#if HAS_TOUCHSCREEN
#if defined(T_WATCH_S3) || defined(ELECROW)
lgfx::Touch_FT5x06 _touch_instance;
#elif defined(HELTEC_V4_TFT)
lgfx::TOUCH_CHSC6X _touch_instance;
#else
lgfx::Touch_GT911 _touch_instance;
#endif
@@ -464,9 +522,9 @@ class LGFX : public lgfx::LGFX_Device
{ // Set the display panel control.
auto cfg = _panel_instance.config(); // Gets a structure for display panel settings.
cfg.pin_cs = ST7789_CS; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = -1; // Pin number where RST is connected (-1 = disable)
cfg.pin_busy = -1; // Pin number where BUSY is connected (-1 = disable)
cfg.pin_cs = ST7789_CS; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = ST7789_RESET; // Pin number where RST is connected (-1 = disable)
cfg.pin_busy = ST7789_BUSY; // Pin number where BUSY is connected (-1 = disable)
// The following setting values are general initial values for each panel, so please comment out any
// unknown items and try them.
@@ -1081,9 +1139,6 @@ static LGFX *tft = nullptr;
#endif
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || defined(ST7796_CS) || defined(ILI9341_DRIVER) || \
defined(ILI9342_DRIVER) || defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST72xx_DE) || \
(ARCH_PORTDUINO && HAS_SCREEN != 0)
#include "SPILock.h"
#include "TFTDisplay.h"
#include <SPI.h>
@@ -1219,12 +1274,15 @@ void TFTDisplay::display(bool fromBlank)
x_LastPixelUpdate = x;
}
}
#if defined(HACKADAY_COMMUNICATOR)
tft->draw16bitBeRGBBitmap(x_FirstPixelUpdate, y, &linePixelBuffer[x_FirstPixelUpdate],
(x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1);
#else
// Step 4: Send the changed pixels on this line to the screen as a single block transfer.
// This function accepts pixel data MSB first so it can dump the memory straight out the SPI port.
tft->pushRect(x_FirstPixelUpdate, y, (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1,
&linePixelBuffer[x_FirstPixelUpdate]);
#endif
somethingChanged = true;
}
y++;
@@ -1288,6 +1346,8 @@ void TFTDisplay::sendCommand(uint8_t com)
display(true);
if (portduino_config.displayBacklight.pin > 0)
digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON);
#elif defined(HACKADAY_COMMUNICATOR)
tft->displayOn();
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
tft->wakeup();
tft->powerSaveOff();
@@ -1300,7 +1360,8 @@ void TFTDisplay::sendCommand(uint8_t com)
unphone.backlight(true); // using unPhone library
#endif
#ifdef RAK14014
#elif !defined(M5STACK) && !defined(ST7789_CS) // T-Deck gets brightness set in Screen.cpp in the handleSetOn function
#elif !defined(M5STACK) && !defined(ST7789_CS) && \
!defined(HACKADAY_COMMUNICATOR) // T-Deck gets brightness set in Screen.cpp in the handleSetOn function
tft->setBrightness(172);
#endif
break;
@@ -1312,6 +1373,8 @@ void TFTDisplay::sendCommand(uint8_t com)
tft->clear();
if (portduino_config.displayBacklight.pin > 0)
digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON);
#elif defined(HACKADAY_COMMUNICATOR)
tft->displayOff();
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
tft->sleep();
tft->powerSaveOn();
@@ -1324,7 +1387,7 @@ void TFTDisplay::sendCommand(uint8_t com)
unphone.backlight(false); // using unPhone library
#endif
#ifdef RAK14014
#elif !defined(M5STACK)
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
tft->setBrightness(0);
#endif
break;
@@ -1340,7 +1403,7 @@ void TFTDisplay::setDisplayBrightness(uint8_t _brightness)
{
#ifdef RAK14014
// todo
#else
#elif !defined(HACKADAY_COMMUNICATOR)
tft->setBrightness(_brightness);
LOG_DEBUG("Brightness is set to value: %i ", _brightness);
#endif
@@ -1358,7 +1421,7 @@ bool TFTDisplay::hasTouch(void)
{
#ifdef RAK14014
return true;
#elif !defined(M5STACK)
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
return tft->touch() != nullptr;
#else
return false;
@@ -1377,7 +1440,7 @@ bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
} else {
return false;
}
#elif !defined(M5STACK)
#elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
return tft->getTouch(x, y);
#else
return false;
@@ -1396,6 +1459,12 @@ bool TFTDisplay::connect()
LOG_INFO("Do TFT init");
#ifdef RAK14014
tft = new TFT_eSPI;
#elif defined(HACKADAY_COMMUNICATOR)
bus = new Arduino_ESP32SPI(TFT_DC, TFT_CS, 38 /* SCK */, 21 /* MOSI */, GFX_NOT_DEFINED /* MISO */, HSPI /* spi_num */);
tft = new Arduino_NV3007(bus, 40, 0 /* rotation */, false /* IPS */, 142 /* width */, 428 /* height */, 12 /* col offset 1 */,
0 /* row offset 1 */, 14 /* col offset 2 */, 0 /* row offset 2 */, nv3007_279_init_operations,
sizeof(nv3007_279_init_operations));
#else
tft = new LGFX;
#endif
@@ -1406,8 +1475,15 @@ bool TFTDisplay::connect()
#ifdef UNPHONE
unphone.backlight(true); // using unPhone library
#endif
#ifdef HACKADAY_COMMUNICATOR
bool beginStatus = tft->begin();
if (beginStatus)
LOG_DEBUG("TFT Success!");
else
LOG_ERROR("TFT Fail!");
#else
tft->init();
#endif
#if defined(M5STACK)
tft->setRotation(0);
@@ -1440,4 +1516,4 @@ bool TFTDisplay::connect()
return true;
}
#endif
#endif // USE_TFTDISPLAY

View File

@@ -101,3 +101,23 @@ void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
else
snprintf(timeStr, maxLength, "unknown age");
}
void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs)
{
uint32_t days = uptimeMillis / 86400000;
uint32_t hours = (uptimeMillis % 86400000) / 3600000;
uint32_t mins = (uptimeMillis % 3600000) / 60000;
uint32_t secs = (uptimeMillis % 60000) / 1000;
if (days) {
snprintf(uptimeStr, maxLength, "%s: %ud %uh", prefix, days, hours);
} else if (hours) {
snprintf(uptimeStr, maxLength, "%s: %uh %um", prefix, hours, mins);
} else if (!includeSecs) {
snprintf(uptimeStr, maxLength, "%s: %um", prefix, mins);
} else if (mins) {
snprintf(uptimeStr, maxLength, "%s: %um %us", prefix, mins, secs);
} else {
snprintf(uptimeStr, maxLength, "%s: %us", prefix, secs);
}
}

View File

@@ -24,3 +24,10 @@ bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int
* @param maxLength Maximum length of the resulting string buffer
*/
void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength);
/**
* Get a compact human-readable string that only shows the largest non-zero time components.
* For example, 0 days 1 hour 2 minutes will display as "1h 2m" but 1 day 2 hours 3 minutes
* will display as "1d 2h".
*/
void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs = false);

View File

@@ -1,5 +1,6 @@
#include "VirtualKeyboard.h"
#include "configuration.h"
#if HAS_SCREEN
#include "VirtualKeyboard.h"
#include "graphics/Screen.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
@@ -505,6 +506,9 @@ void VirtualKeyboard::drawKey(OLEDDisplay *display, const VirtualKey &key, bool
centeredTextY -= 1;
}
}
#ifdef MUZI_BASE // Correct issue with character vertical position on MUZI_BASE
centeredTextY -= 2;
#endif
display->drawString(textX, centeredTextY, keyText.c_str());
}
@@ -736,3 +740,4 @@ bool VirtualKeyboard::isTimedOut() const
}
} // namespace graphics
#endif

View File

@@ -194,17 +194,12 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
int line = 0;
#ifdef T_WATCH_S3
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
graphics::ClockRenderer::drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
}
#endif
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
char timeString[16];
int hour = 0;
int minute = 0;
int second = 0;
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
@@ -215,11 +210,11 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
}
bool isPM = hour >= 12;
// hour = hour > 12 ? hour - 12 : hour;
if (config.display.use_12h_clock) {
hour %= 12;
if (hour == 0)
if (hour == 0) {
hour = 12;
}
snprintf(timeString, sizeof(timeString), "%d:%02d", hour, minute);
} else {
snprintf(timeString, sizeof(timeString), "%02d:%02d", hour, minute);
@@ -229,24 +224,56 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
char secondString[8];
snprintf(secondString, sizeof(secondString), "%02d", second);
#ifdef T_WATCH_S3
float scale = 1.5;
#elif defined(CHATTER_2)
float scale = 1.1;
#else
float scale = 0.75;
if (isHighResolution) {
scale = 1.5;
}
#endif
static bool scaleInitialized = false;
static float scale = 0.75f;
static float segmentWidth = SEGMENT_WIDTH * 0.75f;
static float segmentHeight = SEGMENT_HEIGHT * 0.75f;
uint16_t segmentWidth = SEGMENT_WIDTH * scale;
uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
if (!scaleInitialized) {
float screenwidth_target_ratio = 0.80f; // Target 80% of display width (adjustable)
float max_scale = 3.5f; // Safety limit to avoid runaway scaling
float step = 0.05f; // Step increment per iteration
float target_width = display->getWidth() * screenwidth_target_ratio;
float target_height =
display->getHeight() -
(isHighResolution
? 46
: 33); // Be careful adjusting this number, we have to account for header and the text under the time
float calculated_width_size = 0.0f;
float calculated_height_size = 0.0f;
while (true) {
segmentWidth = SEGMENT_WIDTH * scale;
segmentHeight = SEGMENT_HEIGHT * scale;
calculated_width_size = segmentHeight + ((segmentWidth + (segmentHeight * 2) + 4) * 4);
calculated_height_size = segmentHeight + ((segmentHeight + (segmentHeight * 2) + 4) * 2);
if (calculated_width_size >= target_width || calculated_height_size >= target_height || scale >= max_scale) {
break;
}
scale += step;
}
// If we overshot width, back off one step and recompute segment sizes
if (calculated_width_size > target_width || calculated_height_size > target_height) {
scale -= step;
segmentWidth = SEGMENT_WIDTH * scale;
segmentHeight = SEGMENT_HEIGHT * scale;
}
scaleInitialized = true;
}
size_t len = strlen(timeString);
// calculate hours:minutes string width
uint16_t timeStringWidth = strlen(timeString) * 5;
uint16_t timeStringWidth = len * 5; // base spacing between characters
for (uint8_t i = 0; i < strlen(timeString); i++) {
for (size_t i = 0; i < len; i++) {
char character = timeString[i];
if (character == ':') {
@@ -257,19 +284,21 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
}
uint16_t hourMinuteTextX = (display->getWidth() / 2) - (timeStringWidth / 2);
uint16_t startingHourMinuteTextX = hourMinuteTextX;
uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2);
uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2) + 2;
// iterate over characters in hours:minutes string and draw segmented characters
for (uint8_t i = 0; i < strlen(timeString); i++) {
for (size_t i = 0; i < len; i++) {
char character = timeString[i];
if (character == ':') {
drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale);
hourMinuteTextX += segmentHeight + 6;
if (scale >= 2.0f) {
hourMinuteTextX += (uint16_t)(4.5f * scale);
}
} else {
drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character - '0', scale);
@@ -279,34 +308,27 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
hourMinuteTextX += 5;
}
// draw seconds string
// draw seconds string + AM/PM
display->setFont(FONT_SMALL);
int xOffset = (isHighResolution) ? 0 : -1;
if (hour >= 10) {
xOffset += (isHighResolution) ? 32 : 18;
}
int yOffset = (isHighResolution) ? 3 : 1;
#ifdef SENSECAP_INDICATOR
yOffset -= 3;
#endif
#ifdef T_DECK
yOffset -= 5;
#endif
if (config.display.use_12h_clock) {
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
isPM ? "pm" : "am");
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - 1, isPM ? "pm" : "am");
}
#ifndef USE_EINK
xOffset = (isHighResolution) ? 18 : 10;
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - yOffset,
if (scale >= 2.0f) {
xOffset -= (int)(4.5f * scale);
}
display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - 1,
secondString);
#endif
}
void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y)
{
display->drawFastImage(x, y, 18, 14, bluetoothConnectedIcon);
graphics::drawCommonFooter(display, x, y);
}
// Draw an analog clock
@@ -319,11 +341,6 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
int line = 0;
#ifdef T_WATCH_S3
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
}
#endif
// clock face center coordinates
int16_t centerX = display->getWidth() / 2;
int16_t centerY = display->getHeight() / 2;
@@ -516,6 +533,7 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
display->drawLine(centerX, centerY, secondX, secondY);
#endif
}
graphics::drawCommonFooter(display, x, y);
}
} // namespace ClockRenderer

View File

@@ -24,7 +24,6 @@ void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int heig
// UI elements for clock displays
// void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode = true, float scale = 1);
void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y);
} // namespace ClockRenderer

View File

@@ -1,3 +1,5 @@
#include "configuration.h"
#if HAS_SCREEN
#include "CompassRenderer.h"
#include "NodeDB.h"
#include "UIRenderer.h"
@@ -135,3 +137,4 @@ uint16_t getCompassDiam(uint32_t displayWidth, uint32_t displayHeight)
} // namespace CompassRenderer
} // namespace graphics
#endif

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