Compare commits

...

245 Commits

Author SHA1 Message Date
Jonathan Bennett
c53c959cbd Merge branch 'develop' into XEdDSA 2025-12-01 15:07:32 -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
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
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
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
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
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
Jonathan Bennett
14a790cec5 Merge branch 'develop' into XEdDSA 2025-08-27 16:46:57 -05:00
Jonathan Bennett
51b83a2ca2 Update crypto commit hash 2025-08-21 09:05:05 -05:00
Jonathan Bennett
b0812cec27 Merge branch 'master' into XEdDSA 2025-08-19 14:42:09 -05:00
Jonathan Bennett
05526df7c8 Merge branch 'master' into XEdDSA 2025-08-15 14:32:13 -05:00
Ben Meadors
c5c634ee27 Generate a new node identity on key generation (#7628)
* Generate a new node identity on key generation

* Fixes

* Fixes

* Fixes

* Messed up

* Fixes

* Update src/modules/AdminModule.cpp

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

* Update src/mesh/NodeDB.cpp

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

* Figured it out!

* Cleanup

* Update src/mesh/NodeDB.h

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

* Update src/mesh/NodeDB.cpp

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

* Update src/modules/AdminModule.cpp

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

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-14 21:06:55 -05:00
Jonathan Bennett
359338db32 Merge branch 'master' into XEdDSA 2025-08-13 17:13:46 -05:00
Jonathan Bennett
1dfad22f5f Update to Crypto lib in Meshtatic org 2025-08-13 15:39:41 -05:00
Ben Meadors
15e04ef048 Merge branch 'master' into XEdDSA 2025-08-11 06:21:14 -05:00
Jonathan Bennett
99c4096517 Test commit for XEdDSA support 2025-08-11 00:55:51 -05:00
227 changed files with 38988 additions and 11584 deletions

View File

@@ -1,7 +1,7 @@
# trunk-ignore-all(terrascan/AC_DOCKER_0002): Known terrascan issue # 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/DL3008): Do not pin apt package versions
# trunk-ignore-all(hadolint/DL3013): Do not pin pip 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 USER root

View File

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

View File

@@ -100,7 +100,7 @@ runs:
id: version id: version
- name: Store binaries as an artifact - name: Store binaries as an artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip name: firmware-${{ inputs.arch }}-${{ inputs.board }}-${{ steps.version.outputs.long }}.zip
overwrite: true overwrite: true

View File

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

View File

@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
submodules: recursive submodules: recursive
path: meshtasticd path: meshtasticd
@@ -64,7 +64,7 @@ jobs:
PKG_VERSION: ${{ steps.version.outputs.deb }} PKG_VERSION: ${{ steps.version.outputs.deb }}
- name: Store binaries as an artifact - name: Store binaries as an artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src name: firmware-debian-${{ steps.version.outputs.deb }}~${{ inputs.series }}-src
overwrite: true overwrite: true

View File

@@ -22,7 +22,7 @@ jobs:
outputs: outputs:
artifact-id: ${{ steps.upload.outputs.artifact-id }} artifact-id: ${{ steps.upload.outputs.artifact-id }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
with: with:
submodules: recursive submodules: recursive
ref: ${{github.event.pull_request.head.ref}} ref: ${{github.event.pull_request.head.ref}}
@@ -56,7 +56,7 @@ jobs:
ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }} ota_firmware_target: ${{ steps.ota_dir.outputs.tgt || '' }}
- name: Store binaries as an artifact - name: Store binaries as an artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
id: upload id: upload
with: with:
name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip name: firmware-${{ inputs.platform }}-${{ inputs.pio_env }}-${{ inputs.version }}.zip

View File

@@ -1,176 +0,0 @@
name: Build One Arch
on:
workflow_dispatch:
inputs:
# trunk-ignore(checkov/CKV_GHA_7)
arch:
type: choice
options:
- esp32
- esp32s3
- esp32c3
- esp32c6
- nrf52840
- rp2040
- rp2350
- stm32
- native
permissions: read-all
env:
INPUT_ARCH: ${{ github.event.inputs.arch }}
jobs:
setup:
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: |
TARGETS=$(./bin/generate_ci_matrix.py $INPUT_ARCH --level extra)
echo "Name: $GITHUB_REF_NAME Base: $GITHUB_BASE_REF Ref: $GITHUB_REF"
echo "selected_arch=$TARGETS" >> $GITHUB_OUTPUT
outputs:
selected_arch: ${{ steps.jsonStep.outputs.selected_arch }}
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:
if: ${{ github.event_name != 'workflow_dispatch' }}
needs: [setup, version]
strategy:
fail-fast: false
matrix:
build: ${{ fromJson(needs.setup.outputs.selected_arch) }}
uses: ./.github/workflows/build_firmware.yml
with:
version: ${{ needs.version.outputs.long }}
pio_env: ${{ matrix.build.board }}
platform: ${{ matrix.build.arch }}
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
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]
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 }}

View File

@@ -15,7 +15,6 @@ on:
- rp2040 - rp2040
- rp2350 - rp2350
- stm32 - stm32
- native
target: target:
type: string type: string
required: false required: false
@@ -42,10 +41,9 @@ jobs:
- rp2040 - rp2040
- rp2350 - rp2350
- stm32 - stm32
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-python@v6 - uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
@@ -60,13 +58,13 @@ jobs:
echo "Arch: ${{matrix.arch}}" >> $GITHUB_STEP_SUMMARY echo "Arch: ${{matrix.arch}}" >> $GITHUB_STEP_SUMMARY
echo "Ref: $GITHUB_REF" >> $GITHUB_STEP_SUMMARY echo "Ref: $GITHUB_REF" >> $GITHUB_STEP_SUMMARY
echo "Targets:" >> $GITHUB_STEP_SUMMARY echo "Targets:" >> $GITHUB_STEP_SUMMARY
echo $TARGETS >> $GITHUB_STEP_SUMMARY echo $TARGETS | jq -r 'sort_by(.board) |.[] | "- " + .board' >> $GITHUB_STEP_SUMMARY
version: version:
if: ${{ inputs.target != '' }} if: ${{ inputs.target != '' }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Get release version string - name: Get release version string
run: | run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -87,25 +85,6 @@ jobs:
pio_env: ${{ inputs.target }} pio_env: ${{ inputs.target }}
platform: ${{ inputs.arch }} 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
gather-artifacts: gather-artifacts:
permissions: permissions:
contents: write contents: write
@@ -114,12 +93,12 @@ jobs:
needs: [version, build] needs: [version, build]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
ref: ${{github.event.pull_request.head.ref}} ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}} repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v5 - uses: actions/download-artifact@v6
with: with:
path: ./ path: ./
pattern: firmware-*-* pattern: firmware-*-*
@@ -132,7 +111,7 @@ jobs:
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip - name: Repackage in single firmware zip
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }} name: firmware-${{inputs.target}}-${{ needs.version.outputs.long }}
overwrite: true overwrite: true
@@ -148,7 +127,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2 ./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30 retention-days: 30
- uses: actions/download-artifact@v5 - uses: actions/download-artifact@v6
with: with:
pattern: firmware-*-${{ needs.version.outputs.long }} pattern: firmware-*-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -167,7 +146,7 @@ jobs:
run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output run: zip -j -9 -r ./firmware-${{inputs.target}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip - name: Repackage in single elfs zip
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip name: debug-elfs-${{inputs.target}}-${{ needs.version.outputs.long }}.zip
overwrite: true overwrite: true

View File

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

View File

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

View File

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

View File

@@ -35,7 +35,7 @@ jobs:
- check - check
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-python@v6 - uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
@@ -59,7 +59,7 @@ jobs:
version: version:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Get release version string - name: Get release version string
run: | run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -81,7 +81,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }} if: ${{ github.event_name != 'workflow_dispatch' && github.repository == 'meshtastic/firmware' }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Build base - name: Build base
id: base id: base
uses: ./.github/actions/setup-base uses: ./.github/actions/setup-base
@@ -163,12 +163,12 @@ jobs:
needs: [version, build] needs: [version, build]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
ref: ${{github.event.pull_request.head.ref}} ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}} repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v5 - uses: actions/download-artifact@v6
with: with:
path: ./ path: ./
pattern: firmware-${{matrix.arch}}-* pattern: firmware-${{matrix.arch}}-*
@@ -181,7 +181,7 @@ jobs:
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip - name: Repackage in single firmware zip
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true overwrite: true
@@ -197,7 +197,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2 ./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30 retention-days: 30
- uses: actions/download-artifact@v5 - uses: actions/download-artifact@v6
with: with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -216,7 +216,7 @@ jobs:
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip - name: Repackage in single elfs zip
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
overwrite: true overwrite: true
@@ -242,7 +242,7 @@ jobs:
- package-pio-deps-native-tft - package-pio-deps-native-tft
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@@ -261,14 +261,14 @@ jobs:
Autogenerated by github action, developer should edit as required before publishing... Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb - name: Download source deb
uses: actions/download-artifact@v5 uses: actions/download-artifact@v6
with: with:
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
merge-multiple: true merge-multiple: true
path: ./output/debian-src path: ./output/debian-src
- name: Download `native-tft` pio deps - name: Download `native-tft` pio deps
uses: actions/download-artifact@v5 uses: actions/download-artifact@v6
with: with:
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }} pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -311,14 +311,14 @@ jobs:
needs: [release-artifacts, version] needs: [release-artifacts, version]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
- uses: actions/download-artifact@v5 - uses: actions/download-artifact@v6
with: with:
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -335,7 +335,7 @@ jobs:
- name: Zip firmware - name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output 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: with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
merge-multiple: true merge-multiple: true
@@ -366,14 +366,14 @@ jobs:
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32 esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
- uses: actions/download-artifact@v5 - uses: actions/download-artifact@v6
with: with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }} pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true

View File

@@ -17,7 +17,7 @@ jobs:
- check - check
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- uses: actions/setup-python@v6 - uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
@@ -40,7 +40,7 @@ jobs:
version: version:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Get release version string - name: Get release version string
run: | run: |
echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT echo "long=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
@@ -62,7 +62,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ github.event_name != 'workflow_dispatch' }} if: ${{ github.event_name != 'workflow_dispatch' }}
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
- name: Build base - name: Build base
id: base id: base
uses: ./.github/actions/setup-base uses: ./.github/actions/setup-base
@@ -142,12 +142,12 @@ jobs:
needs: [version, build] needs: [version, build]
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
ref: ${{github.event.pull_request.head.ref}} ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}} repository: ${{github.event.pull_request.head.repo.full_name}}
- uses: actions/download-artifact@v5 - uses: actions/download-artifact@v6
with: with:
path: ./ path: ./
pattern: firmware-${{matrix.arch}}-* pattern: firmware-${{matrix.arch}}-*
@@ -160,7 +160,7 @@ jobs:
run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat run: mv -b -t ./ ./bin/device-*.sh ./bin/device-*.bat
- name: Repackage in single firmware zip - name: Repackage in single firmware zip
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
overwrite: true overwrite: true
@@ -176,7 +176,7 @@ jobs:
./Meshtastic_nRF52_factory_erase*.uf2 ./Meshtastic_nRF52_factory_erase*.uf2
retention-days: 30 retention-days: 30
- uses: actions/download-artifact@v5 - uses: actions/download-artifact@v6
with: with:
name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} name: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -195,7 +195,7 @@ jobs:
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output
- name: Repackage in single elfs zip - name: Repackage in single elfs zip
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
overwrite: true overwrite: true
@@ -221,7 +221,7 @@ jobs:
- package-pio-deps-native-tft - package-pio-deps-native-tft
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
@@ -240,14 +240,14 @@ jobs:
Autogenerated by github action, developer should edit as required before publishing... Autogenerated by github action, developer should edit as required before publishing...
- name: Download source deb - name: Download source deb
uses: actions/download-artifact@v5 uses: actions/download-artifact@v6
with: with:
pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src pattern: firmware-debian-${{ needs.version.outputs.deb }}~UNRELEASED-src
merge-multiple: true merge-multiple: true
path: ./output/debian-src path: ./output/debian-src
- name: Download `native-tft` pio deps - name: Download `native-tft` pio deps
uses: actions/download-artifact@v5 uses: actions/download-artifact@v6
with: with:
pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }} pattern: platformio-deps-native-tft-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -290,14 +290,14 @@ jobs:
needs: [release-artifacts, version] needs: [release-artifacts, version]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
- uses: actions/download-artifact@v5 - uses: actions/download-artifact@v6
with: with:
pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }} pattern: firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true
@@ -314,7 +314,7 @@ jobs:
- name: Zip firmware - name: Zip firmware
run: zip -j -9 -r ./firmware-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip ./output 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: with:
name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip name: debug-elfs-${{matrix.arch}}-${{ needs.version.outputs.long }}.zip
merge-multiple: true merge-multiple: true
@@ -345,14 +345,14 @@ jobs:
esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32 esp32,esp32s3,esp32c3,esp32c6,nrf52840,rp2040,rp2350,stm32
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6
with: with:
python-version: 3.x python-version: 3.x
- uses: actions/download-artifact@v5 - uses: actions/download-artifact@v6
with: with:
pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }} pattern: firmware-{${{ env.targets }}}-${{ needs.version.outputs.long }}
merge-multiple: true merge-multiple: true

View File

@@ -14,7 +14,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Trunk Check - name: Trunk Check
uses: trunk-io/trunk-action@v1 uses: trunk-io/trunk-action@v1
@@ -31,7 +31,7 @@ jobs:
pull-requests: write # For trunk to create PRs pull-requests: write # For trunk to create PRs
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
- name: Trunk Upgrade - name: Trunk Upgrade
uses: trunk-io/trunk-action/upgrade@v1 uses: trunk-io/trunk-action/upgrade@v1

View File

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

View File

@@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
submodules: recursive submodules: recursive
ref: ${{github.event.pull_request.head.ref}} ref: ${{github.event.pull_request.head.ref}}
@@ -56,7 +56,7 @@ jobs:
PLATFORMIO_CORE_DIR: pio/core PLATFORMIO_CORE_DIR: pio/core
- name: Store binaries as an artifact - name: Store binaries as an artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }} name: platformio-deps-${{ inputs.pio_env }}-${{ steps.version.outputs.long }}
overwrite: true overwrite: true

View File

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

View File

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

View File

@@ -60,7 +60,10 @@ jobs:
shell: bash shell: bash
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5 uses: actions/checkout@v6
with:
# Always use master branch for version bumps
ref: master
- name: Setup Python - name: Setup Python
uses: actions/setup-python@v6 uses: actions/setup-python@v6

View File

@@ -21,7 +21,7 @@ jobs:
steps: steps:
# step 1 # step 1
- name: clone application source code - name: clone application source code
uses: actions/checkout@v5 uses: actions/checkout@v6
# step 2 # step 2
- name: full scan - name: full scan
@@ -33,7 +33,7 @@ jobs:
# step 3 # step 3
- name: save report as pipeline artifact - name: save report as pipeline artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: report.sarif name: report.sarif
overwrite: true overwrite: true

View File

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

View File

@@ -20,5 +20,7 @@ jobs:
uses: actions/stale@v10.1.0 uses: actions/stale@v10.1.0
with: with:
days-before-stale: 45 days-before-stale: 45
exempt-issue-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.
exempt-pr-labels: pinned,3.0 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 name: Native Simulator Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
with: with:
ref: ${{github.event.pull_request.head.ref}} ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}} repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -59,7 +59,7 @@ jobs:
id: version id: version
- name: Save coverage information - name: Save coverage information
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
if: always() # run this step even if previous step failed if: always() # run this step even if previous step failed
with: with:
name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}.zip name: lcov-coverage-info-native-simulator-test-${{ steps.version.outputs.long }}.zip
@@ -70,7 +70,7 @@ jobs:
name: Native PlatformIO Tests name: Native PlatformIO Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
with: with:
ref: ${{github.event.pull_request.head.ref}} ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}} repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -94,7 +94,7 @@ jobs:
- name: Save test results - name: Save test results
if: always() # run this step even if previous step failed if: always() # run this step even if previous step failed
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: platformio-test-report-${{ steps.version.outputs.long }}.zip name: platformio-test-report-${{ steps.version.outputs.long }}.zip
overwrite: true overwrite: true
@@ -108,7 +108,7 @@ jobs:
sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative. sed -i -e "s#${PWD}#.#" coverage_tests.info # Make paths relative.
- name: Save coverage information - name: Save coverage information
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
if: always() # run this step even if previous step failed if: always() # run this step even if previous step failed
with: with:
name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}.zip name: lcov-coverage-info-native-platformio-tests-${{ steps.version.outputs.long }}.zip
@@ -127,7 +127,7 @@ jobs:
- platformio-tests - platformio-tests
if: always() if: always()
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v6
with: with:
ref: ${{github.event.pull_request.head.ref}} ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}} repository: ${{github.event.pull_request.head.repo.full_name}}
@@ -137,20 +137,20 @@ jobs:
id: version id: version
- name: Download test artifacts - name: Download test artifacts
uses: actions/download-artifact@v5 uses: actions/download-artifact@v6
with: with:
name: platformio-test-report-${{ steps.version.outputs.long }}.zip name: platformio-test-report-${{ steps.version.outputs.long }}.zip
merge-multiple: true merge-multiple: true
- name: Test Report - name: Test Report
uses: dorny/test-reporter@v2.1.1 uses: dorny/test-reporter@v2.2.0
with: with:
name: PlatformIO Tests name: PlatformIO Tests
path: testreport.xml path: testreport.xml
reporter: java-junit reporter: java-junit
- name: Download coverage artifacts - name: Download coverage artifacts
uses: actions/download-artifact@v5 uses: actions/download-artifact@v6
with: with:
pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}.zip pattern: lcov-coverage-info-native-*-${{ steps.version.outputs.long }}.zip
path: code-coverage-report path: code-coverage-report
@@ -163,7 +163,7 @@ jobs:
genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report genhtml --quiet --legend --prefix "${PWD}" code-coverage-report/coverage_src.info --output-directory code-coverage-report
- name: Save Code Coverage Report - name: Save Code Coverage Report
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v5
with: with:
name: code-coverage-report-${{ steps.version.outputs.long }}.zip name: code-coverage-report-${{ steps.version.outputs.long }}.zip
path: code-coverage-report path: code-coverage-report

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v5 uses: actions/checkout@v6
with: with:
submodules: true submodules: true

View File

@@ -4,31 +4,31 @@ cli:
plugins: plugins:
sources: sources:
- id: trunk - id: trunk
ref: v1.7.3 ref: v1.7.4
uri: https://github.com/trunk-io/plugins uri: https://github.com/trunk-io/plugins
lint: lint:
enabled: enabled:
- checkov@3.2.477 - checkov@3.2.495
- renovate@41.144.1 - renovate@42.24.1
- prettier@3.6.2 - prettier@3.6.2
- trufflehog@3.90.8 - trufflehog@3.91.1
- yamllint@1.37.1 - yamllint@1.37.1
- bandit@1.8.6 - bandit@1.9.2
- trivy@0.67.1 - trivy@0.67.2
- taplo@0.10.0 - taplo@0.10.0
- ruff@0.14.0 - ruff@0.14.6
- isort@6.1.0 - isort@7.0.0
- markdownlint@0.45.0 - markdownlint@0.46.0
- oxipng@9.1.5 - oxipng@9.1.5
- svgo@4.0.0 - svgo@4.0.0
- actionlint@1.7.7 - actionlint@1.7.9
- flake8@7.3.0 - flake8@7.3.0
- hadolint@2.14.0 - hadolint@2.14.0
- shfmt@3.6.0 - shfmt@3.6.0
- shellcheck@0.11.0 - shellcheck@0.11.0
- black@25.9.0 - black@25.11.0
- git-diff-check - git-diff-check
- gitleaks@8.28.0 - gitleaks@8.30.0
- clang-format@16.0.3 - clang-format@16.0.3
ignore: ignore:
- linters: [ALL] - linters: [ALL]

View File

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

View File

@@ -8,7 +8,8 @@ ARG PIO_ENV=native
ENV PIP_ROOT_USER_ACTION=ignore ENV PIP_ROOT_USER_ACTION=ignore
RUN apk --no-cache add \ 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 \ libusb-dev i2c-tools-dev libuv-dev openssl-dev pkgconf argp-standalone \
libx11-dev libinput-dev libxkbcommon-dev \ libx11-dev libinput-dev libxkbcommon-dev \
&& rm -rf /var/cache/apk/* \ && rm -rf /var/cache/apk/* \
@@ -40,8 +41,8 @@ LABEL org.opencontainers.image.title="Meshtastic" \
USER root USER root
RUN apk --no-cache add \ RUN apk --no-cache add \
shadow libstdc++ libgpiod yaml-cpp libusb i2c-tools libuv \ shadow libstdc++ libbsd libgpiod yaml-cpp libusb \
libx11 libinput libxkbcommon \ i2c-tools libuv libx11 libinput libxkbcommon \
&& rm -rf /var/cache/apk/* \ && rm -rf /var/cache/apk/* \
&& mkdir -p /var/lib/meshtasticd \ && mkdir -p /var/lib/meshtasticd \
&& mkdir -p /etc/meshtasticd/config.d \ && mkdir -p /etc/meshtasticd/config.d \

View File

@@ -61,7 +61,7 @@ lib_deps =
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master # renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
rweather/Crypto@0.4.0 https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
lib_ignore = lib_ignore =
segger_rtt segger_rtt

View File

@@ -32,7 +32,7 @@ lib_deps =
# renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master # renovate: datasource=git-refs depName=meshtastic-ESP32_Codec2 packageName=https://github.com/meshtastic/ESP32_Codec2 gitBranch=master
https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip https://github.com/meshtastic/ESP32_Codec2/archive/633326c78ac251c059ab3a8c430fcdf25b41672f.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
rweather/Crypto@0.4.0 https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
build_src_filter = build_src_filter =
${esp32_base.build_src_filter} -<mesh/http> ${esp32_base.build_src_filter} -<mesh/http>

View File

@@ -7,7 +7,7 @@ extends = arduino_base
platform_packages = platform_packages =
; our custom Git version until they merge our PR ; our custom Git version until they merge our PR
# TODO renovate # TODO renovate
platformio/framework-arduinoadafruitnrf52 @ https://github.com/meshtastic/Adafruit_nRF52_Arduino#e13f5820002a4fb2a5e6754b42ace185277e5adf platformio/framework-arduinoadafruitnrf52 @ https://github.com/meshtastic/Adafruit_nRF52_Arduino#c770c8a16a351b55b86e347a3d9d7b74ad0bbf39
; Don't renovate toolchain-gccarmnoneeabi ; Don't renovate toolchain-gccarmnoneeabi
platformio/toolchain-gccarmnoneeabi@~1.90301.0 platformio/toolchain-gccarmnoneeabi@~1.90301.0
@@ -29,7 +29,7 @@ lib_deps=
${arduino_base.lib_deps} ${arduino_base.lib_deps}
${radiolib_base.lib_deps} ${radiolib_base.lib_deps}
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
rweather/Crypto@0.4.0 https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
lib_ignore = lib_ignore =
BluetoothOTA BluetoothOTA

View File

@@ -8,7 +8,7 @@ lib_deps =
${environmental_base.lib_deps} ${environmental_base.lib_deps}
${environmental_extra.lib_deps} ${environmental_extra.lib_deps}
# renovate: datasource=git-refs depName=Kongduino-Adafruit_nRFCrypto packageName=https://github.com/Kongduino/Adafruit_nRFCrypto gitBranch=master # renovate: datasource=git-refs depName=Kongduino-Adafruit_nRFCrypto packageName=https://github.com/Kongduino/Adafruit_nRFCrypto gitBranch=master
https://github.com/Kongduino/Adafruit_nRFCrypto/archive/5f838d2709461a2c981f642917aa50254a25c14c.zip https://github.com/Kongduino/Adafruit_nRFCrypto/archive/8cde7189b5ead9dcd49f72601b43b969c0bbc06e.zip
; Common NRF52 debugging settings follow. See the Meshtastic developer docs for how to connect SWD debugging probes to your board. ; Common NRF52 debugging settings follow. See the Meshtastic developer docs for how to connect SWD debugging probes to your board.

View File

@@ -2,7 +2,7 @@
[portduino_base] [portduino_base]
platform = platform =
# renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop # renovate: datasource=git-refs depName=platform-native packageName=https://github.com/meshtastic/platform-native gitBranch=develop
https://github.com/meshtastic/platform-native/archive/d3f6e339534233c7217818867368767590ce549e.zip https://github.com/meshtastic/platform-native/archive/f566d364204416cdbf298e349213f7d551f793d9.zip
framework = arduino framework = arduino
build_src_filter = build_src_filter =
@@ -24,7 +24,8 @@ lib_deps =
${radiolib_base.lib_deps} ${radiolib_base.lib_deps}
${environmental_base.lib_deps} ${environmental_base.lib_deps}
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
rweather/Crypto@0.4.0 #rweather/Crypto@0.4.0
https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
# renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX # renovate: datasource=custom.pio depName=LovyanGFX packageName=lovyan03/library/LovyanGFX
lovyan03/LovyanGFX@^1.2.0 lovyan03/LovyanGFX@^1.2.0
# renovate: datasource=git-refs depName=libch341-spi-userspace packageName=https://github.com/pine64/libch341-spi-userspace gitBranch=main # renovate: datasource=git-refs depName=libch341-spi-userspace packageName=https://github.com/pine64/libch341-spi-userspace gitBranch=main

View File

@@ -31,4 +31,4 @@ lib_deps =
${environmental_extra.lib_deps} ${environmental_extra.lib_deps}
${radiolib_base.lib_deps} ${radiolib_base.lib_deps}
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
rweather/Crypto@0.4.0 https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip

View File

@@ -28,4 +28,4 @@ lib_deps =
${environmental_extra.lib_deps} ${environmental_extra.lib_deps}
${radiolib_base.lib_deps} ${radiolib_base.lib_deps}
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto # renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
rweather/Crypto@0.4.0 https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip

View File

@@ -2,7 +2,7 @@
extends = arduino_base extends = arduino_base
platform = platform =
# renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32 # renovate: datasource=custom.pio depName=platformio/ststm32 packageName=platformio/platform/ststm32
platformio/ststm32@19.3.0 platformio/ststm32@19.4.0
platform_packages = platform_packages =
# TODO renovate # TODO renovate
platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32/archive/2.10.1.zip
@@ -37,6 +37,9 @@ build_flags =
-DRADIOLIB_EXCLUDE_LR11X0=1 -DRADIOLIB_EXCLUDE_LR11X0=1
-DHAL_DAC_MODULE_ONLY -DHAL_DAC_MODULE_ONLY
-DHAL_RNG_MODULE_ENABLED -DHAL_RNG_MODULE_ENABLED
-Wl,--wrap=__assert_func
-Wl,--wrap=strerror
-Wl,--wrap=_tzset_unlocked_r
build_src_filter = build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/RemoteHardwareModule.cpp> -<platform/nrf52> -<platform/portduino> -<platform/rp2xx0> -<mesh/raspihttp> ${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/RemoteHardwareModule.cpp> -<platform/nrf52> -<platform/portduino> -<platform/rp2xx0> -<mesh/raspihttp>
@@ -50,7 +53,7 @@ lib_deps =
${radiolib_base.lib_deps} ${radiolib_base.lib_deps}
# renovate: datasource=git-refs depName=caveman99-stm32-Crypto packageName=https://github.com/caveman99/Crypto gitBranch=main # renovate: datasource=git-refs depName=caveman99-stm32-Crypto packageName=https://github.com/caveman99/Crypto gitBranch=main
https://github.com/caveman99/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip https://github.com/meshtastic/Crypto/archive/1aa30eb536bd52a576fde6dfa393bf7349cf102d.zip
lib_ignore = lib_ignore =
OneButton OneButton

View File

@@ -15,12 +15,12 @@ SET "LOGCOUNTER=0"
SET "BPS_RESET=0" SET "BPS_RESET=0"
@REM FIXME: Determine mcu from PlatformIO variant, this is unmaintainable. @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 "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 heltec-v4"
SET "C3=esp32c3" SET "C3=esp32c3"
@REM FIXME: Determine flash size from PlatformIO variant, this is unmaintainable. @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 "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 "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" 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 heltec-v4"
GOTO getopts GOTO getopts
:help :help

View File

@@ -31,21 +31,23 @@ MUIDB_8MB=(
"seeed-sensecap-indicator" "seeed-sensecap-indicator"
) )
BIGDB_16MB=( BIGDB_16MB=(
"t-deck"
"mesh-tab"
"t-energy-s3"
"dreamcatcher" "dreamcatcher"
"ESP32-S3-Pico"
"m5stack-cores3"
"station-g2"
"t-eth-elite"
"tlora-pager"
"t-watch-s3"
"elecrow-adv" "elecrow-adv"
"ESP32-S3-Pico"
"heltec-v4"
"m5stack-cores3"
"mesh-tab"
"station-g2"
"t-deck"
"t-energy-s3"
"t-eth-elite"
"t-watch-s3"
"tlora-pager"
) )
S3_VARIANTS=( S3_VARIANTS=(
"s3" "s3"
"-v3" "-v3"
"-v4"
"t-deck" "t-deck"
"wireless-paper" "wireless-paper"
"wireless-tracker" "wireless-tracker"

View File

@@ -87,6 +87,15 @@
</screenshots> </screenshots>
<releases> <releases>
<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"> <release version="2.7.13" date="2025-10-11">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.13</url> <url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.13</url>
</release> </release>

View File

@@ -1 +1 @@
2.6.6 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"
}

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
}
}

19
debian/changelog vendored
View File

@@ -1,3 +1,22 @@
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 meshtasticd (2.7.13.0) unstable; urgency=medium
* Version 2.7.13 * Version 2.7.13

1
debian/control vendored
View File

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

View File

@@ -33,6 +33,7 @@ BuildRequires: python3dist(grpcio[protobuf])
BuildRequires: python3dist(grpcio-tools) BuildRequires: python3dist(grpcio-tools)
BuildRequires: git-core BuildRequires: git-core
BuildRequires: gcc-c++ BuildRequires: gcc-c++
BuildRequires: (glibc-devel >= 2.38) or pkgconfig(libbsd-overlay)
BuildRequires: pkgconfig(yaml-cpp) BuildRequires: pkgconfig(yaml-cpp)
BuildRequires: pkgconfig(libgpiod) BuildRequires: pkgconfig(libgpiod)
BuildRequires: pkgconfig(bluez) BuildRequires: pkgconfig(bluez)
@@ -49,6 +50,13 @@ BuildRequires: pkgconfig(x11)
BuildRequires: pkgconfig(libinput) BuildRequires: pkgconfig(libinput)
BuildRequires: pkgconfig(xkbcommon-x11) 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 Requires: systemd-udev
%description %description

View File

@@ -62,7 +62,7 @@ monitor_speed = 115200
monitor_filters = direct monitor_filters = direct
lib_deps = lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master # 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 # renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip 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 # renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
@@ -90,7 +90,7 @@ framework = arduino
lib_deps = lib_deps =
${env.lib_deps} ${env.lib_deps}
# renovate: datasource=custom.pio depName=NonBlockingRTTTL packageName=end2endzone/library/NonBlockingRTTTL # 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_flags = ${env.build_flags} -Os
build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/> build_src_filter = ${env.build_src_filter} -<platform/portduino/> -<graphics/niche/>
@@ -115,12 +115,13 @@ lib_deps =
[radiolib_base] [radiolib_base]
lib_deps = lib_deps =
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib # 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] [device-ui_base]
lib_deps = lib_deps =
# renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master # renovate: datasource=git-refs depName=meshtastic/device-ui packageName=https://github.com/meshtastic/device-ui gitBranch=master
https://github.com/meshtastic/device-ui/archive/19b7855e9a1d9deff37391659ca7194e4ef57c43.zip https://github.com/meshtastic/device-ui/archive/28167c67dfd13015a0b5eef1828f95fe8e3ab7c3.zip
; Common libs for environmental measurements in telemetry module ; Common libs for environmental measurements in telemetry module
[environmental_base] [environmental_base]
@@ -164,11 +165,11 @@ lib_deps =
# renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass # renovate: datasource=custom.pio depName=QMC5883L Compass packageName=mprograms/library/QMC5883LCompass
mprograms/QMC5883LCompass@1.2.3 mprograms/QMC5883LCompass@1.2.3
# renovate: datasource=custom.pio depName=DFRobot_RTU packageName=dfrobot/library/DFRobot_RTU # 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 # 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 https://github.com/DFRobot/DFRobot_RainfallSensor/archive/38fea5e02b40a5430be6dab39a99a6f6347d667e.zip
# renovate: datasource=custom.pio depName=INA226 packageName=robtillaart/library/INA226 # 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 # 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 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 # renovate: datasource=custom.pio depName=SparkFun 9DoF IMU Breakout ICM 20948 packageName=sparkfun/library/SparkFun 9DoF IMU Breakout - ICM 20948 - Arduino Library
@@ -176,11 +177,13 @@ lib_deps =
# renovate: datasource=custom.pio depName=Adafruit LTR390 Library packageName=adafruit/library/Adafruit LTR390 Library # renovate: datasource=custom.pio depName=Adafruit LTR390 Library packageName=adafruit/library/Adafruit LTR390 Library
adafruit/Adafruit LTR390 Library@1.1.2 adafruit/Adafruit LTR390 Library@1.1.2
# renovate: datasource=custom.pio depName=Adafruit PCT2075 packageName=adafruit/library/Adafruit PCT2075 # 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 # renovate: datasource=custom.pio depName=DFRobot_BMM150 packageName=dfrobot/library/DFRobot_BMM150
dfrobot/DFRobot_BMM150@1.0.0 dfrobot/DFRobot_BMM150@1.0.0
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561 # renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
adafruit/Adafruit TSL2561@1.1.2 adafruit/Adafruit TSL2561@1.1.2
# renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/BH1750_WE@^1.1.10
wollewald/BH1750_WE@^1.1.10
; (not included in native / portduino) ; (not included in native / portduino)
[environmental_extra] [environmental_extra]
@@ -210,6 +213,6 @@ lib_deps =
# renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master # renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
# renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core # 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 # renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
sensirion/Sensirion I2C SCD4x@1.1.0 sensirion/Sensirion I2C SCD4x@1.1.0

View File

@@ -14,16 +14,16 @@ class NodeStatus : public Status
CallbackObserver<NodeStatus, const NodeStatus *> statusObserver = CallbackObserver<NodeStatus, const NodeStatus *> statusObserver =
CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus); CallbackObserver<NodeStatus, const NodeStatus *>(this, &NodeStatus::updateStatus);
uint8_t numOnline = 0; uint16_t numOnline = 0;
uint8_t numTotal = 0; uint16_t numTotal = 0;
uint8_t lastNumTotal = 0; uint16_t lastNumTotal = 0;
public: public:
bool forceUpdate = false; bool forceUpdate = false;
NodeStatus() { statusType = STATUS_TYPE_NODE; } 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->forceUpdate = forceUpdate;
this->numOnline = numOnline; this->numOnline = numOnline;
@@ -34,11 +34,11 @@ class NodeStatus : public Status
void observe(Observable<const NodeStatus *> *source) { statusObserver.observe(source); } 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 bool matches(const NodeStatus *newStatus) const
{ {
@@ -56,7 +56,7 @@ class NodeStatus : public Status
numTotal = newStatus->getNumTotal(); numTotal = newStatus->getNumTotal();
} }
if (isDirty || newStatus->forceUpdate) { 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); onNewStatus.notifyObservers(this);
} }
return 0; return 0;

View File

@@ -194,7 +194,7 @@ static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level se
#ifdef BATTERY_PIN #ifdef BATTERY_PIN
static void adcEnable() void battery_adcEnable()
{ {
#ifdef ADC_CTRL // enable adc voltage divider when we need to read #ifdef ADC_CTRL // enable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP #ifdef ADC_USE_PULLUP
@@ -214,7 +214,7 @@ static void adcEnable()
#endif #endif
} }
static void adcDisable() static void battery_adcDisable()
{ {
#ifdef ADC_CTRL // disable adc voltage divider when we need to read #ifdef ADC_CTRL // disable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP #ifdef ADC_USE_PULLUP
@@ -278,6 +278,11 @@ class AnalogBatteryLevel : public HasBatteryLevel
break; 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); return clamp((int)(battery_SOC), 0, 100);
} }
@@ -320,7 +325,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
uint32_t raw = 0; uint32_t raw = 0;
float scaled = 0; float scaled = 0;
adcEnable(); battery_adcEnable();
#ifdef ARCH_ESP32 // ADC block for espressif platforms #ifdef ARCH_ESP32 // ADC block for espressif platforms
raw = espAdcRead(); raw = espAdcRead();
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs); scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
@@ -332,7 +337,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
raw = raw / BATTERY_SENSE_SAMPLES; raw = raw / BATTERY_SENSE_SAMPLES;
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw; scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
#endif #endif
adcDisable(); battery_adcDisable();
if (!initial_read_done) { if (!initial_read_done) {
// Flush the smoothing filter with an ADC reading, if the reading is plausibly correct // 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 // if it's not HIGH - check the battery
#endif #endif
#elif defined(MUZI_BASE)
return NRF_POWER->USBREGSTATUS & POWER_USBREGSTATUS_VBUSDETECT_Msk;
#endif #endif
return getBattVoltage() > chargingVolt; return getBattVoltage() > chargingVolt;
} }
@@ -470,6 +477,8 @@ class AnalogBatteryLevel : public HasBatteryLevel
#endif #endif
#ifdef EXT_CHRG_DETECT #ifdef EXT_CHRG_DETECT
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value; return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
#elif defined(BATTERY_CHARGING_INV)
return !digitalRead(BATTERY_CHARGING_INV);
#else #else
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION) #if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(DISABLE_INA_CHARGING_DETECTION)
if (hasINA()) { if (hasINA()) {
@@ -698,11 +707,18 @@ bool Power::setup()
[]() { []() {
power->setIntervalFromNow(0); power->setIntervalFromNow(0);
runASAP = true; runASAP = true;
BaseType_t higherWake = 0;
}, },
CHANGE); CHANGE);
#endif #endif
#ifdef BATTERY_CHARGING_INV
attachInterrupt(
BATTERY_CHARGING_INV,
[]() {
power->setIntervalFromNow(0);
runASAP = true;
},
CHANGE);
#endif
enabled = found; enabled = found;
low_voltage_counter = 0; low_voltage_counter = 0;
@@ -759,6 +775,8 @@ void Power::shutdown()
if (screen) { if (screen) {
#ifdef T_DECK_PRO #ifdef T_DECK_PRO
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button 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 #else
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
#endif #endif
@@ -839,8 +857,11 @@ void Power::readPowerStatus()
// Notify any status instances that are observing us // Notify any status instances that are observing us
const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isChargingNow, batteryVoltageMv, batteryChargePercent); const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isChargingNow, batteryVoltageMv, batteryChargePercent);
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(), powerStatus2.getIsCharging(), if (millis() > lastLogTime + 50 * 1000) {
powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent()); LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d", powerStatus2.getHasUSB(),
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
lastLogTime = millis();
}
newStatus.notifyObservers(&powerStatus2); newStatus.notifyObservers(&powerStatus2);
#ifdef DEBUG_HEAP #ifdef DEBUG_HEAP
if (lastheap != memGet.getFreeHeap()) { if (lastheap != memGet.getFreeHeap()) {
@@ -903,13 +924,8 @@ void Power::readPowerStatus()
low_voltage_counter++; low_voltage_counter++;
LOG_DEBUG("Low voltage counter: %d/10", low_voltage_counter); LOG_DEBUG("Low voltage counter: %d/10", low_voltage_counter);
if (low_voltage_counter > 10) { 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"); LOG_INFO("Low voltage detected, trigger deep sleep");
powerFSM.trigger(EVENT_LOW_BATTERY); powerFSM.trigger(EVENT_LOW_BATTERY);
#endif
} }
} else { } else {
low_voltage_counter = 0; low_voltage_counter = 0;
@@ -1437,7 +1453,7 @@ class LipoCharger : public HasBatteryLevel
/** /**
* return true if there is an external power source detected * 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 * return true if the battery is currently charging
@@ -1549,4 +1565,4 @@ bool Power::meshSolarInit()
{ {
return false; return false;
} }
#endif #endif

View File

@@ -57,21 +57,21 @@ static bool isPowered()
static void sdsEnter() 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 // 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); doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, false);
} }
static void lowBattSDSEnter() static void lowBattSDSEnter()
{ {
LOG_DEBUG("State: Lower batt SDS"); LOG_POWERFSM("State: Lower batt SDS");
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, true); doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false, true);
} }
extern Power *power; extern Power *power;
static void shutdownEnter() static void shutdownEnter()
{ {
LOG_DEBUG("State: SHUTDOWN"); LOG_POWERFSM("State: SHUTDOWN");
shutdownAtMsec = millis(); shutdownAtMsec = millis();
} }
@@ -81,7 +81,7 @@ static uint32_t secsSlept;
static void lsEnter() 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) if (screen)
screen->setOn(false); screen->setOn(false);
secsSlept = 0; // How long have we been sleeping this time secsSlept = 0; // How long have we been sleeping this time
@@ -155,12 +155,12 @@ static void lsIdle()
static void lsExit() static void lsExit()
{ {
LOG_INFO("Exit state: LS"); LOG_POWERFSM("State: lsExit");
} }
static void nbEnter() static void nbEnter()
{ {
LOG_DEBUG("State: NB"); LOG_POWERFSM("State: nbEnter");
if (screen) if (screen)
screen->setOn(false); screen->setOn(false);
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
@@ -173,6 +173,7 @@ static void nbEnter()
static void darkEnter() static void darkEnter()
{ {
LOG_POWERFSM("State: darkEnter");
setBluetoothEnable(true); setBluetoothEnable(true);
if (screen) if (screen)
screen->setOn(false); screen->setOn(false);
@@ -180,7 +181,7 @@ static void darkEnter()
static void serialEnter() static void serialEnter()
{ {
LOG_DEBUG("State: SERIAL"); LOG_POWERFSM("State: serialEnter");
setBluetoothEnable(false); setBluetoothEnable(false);
if (screen) { if (screen) {
screen->setOn(true); screen->setOn(true);
@@ -189,13 +190,14 @@ static void serialEnter()
static void serialExit() static void serialExit()
{ {
LOG_POWERFSM("State: serialExit");
// Turn bluetooth back on when we leave serial stream API // Turn bluetooth back on when we leave serial stream API
setBluetoothEnable(true); setBluetoothEnable(true);
} }
static void powerEnter() static void powerEnter()
{ {
// LOG_DEBUG("State: POWER"); LOG_POWERFSM("State: powerEnter");
if (!isPowered()) { if (!isPowered()) {
// If we got here, we are in the wrong state - we should be in powered, let that state handle things // 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"); LOG_INFO("Loss of power in Powered");
@@ -210,6 +212,7 @@ static void powerEnter()
static void powerIdle() static void powerIdle()
{ {
// LOG_POWERFSM("State: powerIdle"); // very chatty
if (!isPowered()) { if (!isPowered()) {
// If we got here, we are in the wrong state // If we got here, we are in the wrong state
LOG_INFO("Loss of power in Powered"); LOG_INFO("Loss of power in Powered");
@@ -219,14 +222,13 @@ static void powerIdle()
static void powerExit() static void powerExit()
{ {
if (screen) LOG_POWERFSM("State: powerExit");
screen->setOn(true);
setBluetoothEnable(true); setBluetoothEnable(true);
} }
static void onEnter() static void onEnter()
{ {
LOG_DEBUG("State: ON"); LOG_POWERFSM("State: onEnter");
if (screen) if (screen)
screen->setOn(true); screen->setOn(true);
setBluetoothEnable(true); setBluetoothEnable(true);
@@ -234,6 +236,7 @@ static void onEnter()
static void onIdle() static void onIdle()
{ {
LOG_POWERFSM("State: onIdle");
if (isPowered()) { if (isPowered()) {
// If we got here, we are in the wrong state - we should be in powered, let that state handle things // 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); powerFSM.trigger(EVENT_POWER_CONNECTED);
@@ -242,7 +245,7 @@ static void onIdle()
static void bootEnter() static void bootEnter()
{ {
LOG_DEBUG("State: BOOT"); LOG_POWERFSM("State: bootEnter");
} }
State stateSHUTDOWN(shutdownEnter, NULL, NULL, "SHUTDOWN"); 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 // 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"); 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 // Show the received text message
powerFSM.add_transition(&stateLS, &stateON, EVENT_RECEIVED_MSG, NULL, "Received text"); powerFSM.add_transition(&stateLS, &stateON, EVENT_RECEIVED_MSG, NULL, "Received text");
powerFSM.add_transition(&stateNB, &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 // 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 // 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 || 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_TAK_TRACKER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR; config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR;

View File

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

View File

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

View File

@@ -126,6 +126,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 #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 #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 // Default system gain to 0 if not defined
#ifndef TX_GAIN_LORA #ifndef TX_GAIN_LORA
#define TX_GAIN_LORA 0 #define TX_GAIN_LORA 0
@@ -223,6 +228,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define ICM20948_ADDR_ALT 0x68 #define ICM20948_ADDR_ALT 0x68
#define BHI260AP_ADDR 0x28 #define BHI260AP_ADDR 0x28
#define BMM150_ADDR 0x13 #define BMM150_ADDR 0x13
#define DA217_ADDR 0x26
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// LED // LED
@@ -244,7 +250,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Touchscreen // Touchscreen
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#define FT6336U_ADDR 0x48 #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) // RAK12035VB Soil Monitor (using RAK12023 up to 3 RAK12035 monitors can be connected)
@@ -389,6 +397,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define HAS_RGB_LED #define HAS_RGB_LED
#endif #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 // default mapping of pins
#if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN) #if defined(PIN_BUTTON2) && !defined(CANCEL_BUTTON_PIN)
#define ALT_BUTTON_PIN PIN_BUTTON2 #define ALT_BUTTON_PIN PIN_BUTTON2

View File

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

View File

@@ -106,6 +106,7 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation
if (i2cBus->available()) if (i2cBus->available())
i2cBus->read(); i2cBus->read();
} }
LOG_DEBUG("Register value: 0x%x", value);
return value; return value;
} }
@@ -377,14 +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: // same as OPT3001_ADDR_ALT
case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2); registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2);
if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0x11f3 || registerValue == 0xe9c || if (registerValue == 0x5449) {
registerValue == 0xc8d) {
type = SHT4X;
logFoundDevice("SHT4X", (uint8_t)addr.address);
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
type = OPT3001; type = OPT3001;
logFoundDevice("OPT3001", (uint8_t)addr.address); 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 { } else {
type = SHT31; type = SHT31;
logFoundDevice("SHT31", (uint8_t)addr.address); logFoundDevice("SHT31", (uint8_t)addr.address);
@@ -465,8 +465,23 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
break; break;
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address); 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); 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: case TSL25911_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x12), 1); registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x12), 1);
if (registerValue == 0x50) { if (registerValue == 0x50) {
@@ -484,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(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(LTR390UV_ADDR, LTR390UV, "LTR390UV", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(PCT2075_ADDR, PCT2075, "PCT2075", (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); case CST328_ADDR:
SCAN_SIMPLE_CASE(LTR553ALS_ADDR, LTR553ALS, "LTR553ALS", (uint8_t)addr.address); // 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(BHI260AP_ADDR, BHI260AP, "BHI260AP", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(SCD4X_ADDR, SCD4X, "SCD4X", (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); SCAN_SIMPLE_CASE(BMM150_ADDR, BMM150, "BMM150", (uint8_t)addr.address);
@@ -494,8 +539,12 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
#endif #endif
case MLX90614_ADDR_DEF: case MLX90614_ADDR_DEF:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x0e), 1); // Do we have the MLX90614 or the MPR121KB or the CST226SE
if (registerValue == 0x5a) { 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; type = MLX90614;
logFoundDevice("MLX90614", (uint8_t)addr.address); logFoundDevice("MLX90614", (uint8_t)addr.address);
} else { } else {
@@ -513,6 +562,11 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
case ICM20948_ADDR: // same as BMX160_ADDR case ICM20948_ADDR: // same as BMX160_ADDR
case ICM20948_ADDR_ALT: // same as MPU6050_ADDR case ICM20948_ADDR_ALT: // same as MPU6050_ADDR
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1); registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
#ifdef HAS_ICM20948
type = ICM20948;
logFoundDevice("ICM20948", (uint8_t)addr.address);
break;
#endif
if (registerValue == 0xEA) { if (registerValue == 0xEA) {
type = ICM20948; type = ICM20948;
logFoundDevice("ICM20948", (uint8_t)addr.address); logFoundDevice("ICM20948", (uint8_t)addr.address);

View File

@@ -38,14 +38,16 @@ template <typename T, std::size_t N> std::size_t array_count(const T (&)[N])
return N; return N;
} }
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL) #ifndef GPS_SERIAL_PORT
#if defined(GPS_SERIAL_PORT) #define GPS_SERIAL_PORT Serial1
HardwareSerial *GPS::_serial_gps = &GPS_SERIAL_PORT;
#else
HardwareSerial *GPS::_serial_gps = &Serial1;
#endif #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) #elif defined(ARCH_RP2040)
SerialUART *GPS::_serial_gps = &Serial1; SerialUART *GPS::_serial_gps = &GPS_SERIAL_PORT;
#else #else
HardwareSerial *GPS::_serial_gps = nullptr; HardwareSerial *GPS::_serial_gps = nullptr;
#endif #endif
@@ -240,6 +242,9 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
buffer[bytesRead] = b; buffer[bytesRead] = b;
bytesRead++; bytesRead++;
if ((bytesRead == 767) || (b == '\r')) { if ((bytesRead == 767) || (b == '\r')) {
#ifdef GPS_DEBUG
LOG_DEBUG(debugmsg.c_str());
#endif
if (strnstr((char *)buffer, message, bytesRead) != nullptr) { if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
#ifdef GPS_DEBUG #ifdef GPS_DEBUG
LOG_DEBUG("Found: %s", message); // Log the found message 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; return GNSS_RESPONSE_OK;
} else { } else {
bytesRead = 0; bytesRead = 0;
#ifdef GPS_DEBUG
LOG_DEBUG(debugmsg.c_str());
#endif
} }
} }
} }
@@ -1275,6 +1277,24 @@ GnssModel_t GPS::probe(int serialSpeed)
memset(&ublox_info, 0, sizeof(ublox_info)); memset(&ublox_info, 0, sizeof(ublox_info));
delay(100); delay(100);
#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);
// 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) // 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"); _serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20); delay(20);
@@ -1473,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')) { 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 // check if we can see our chips
for (const auto &chipInfo : responseMap) { for (const auto &chipInfo : responseMap) {
if (strstr(response, chipInfo.detectionString.c_str()) != nullptr) { if (strstr(response, chipInfo.detectionString.c_str()) != nullptr) {
#ifdef GPS_DEBUG
LOG_DEBUG(response);
#endif
LOG_INFO("%s detected", chipInfo.chipName.c_str()); LOG_INFO("%s detected", chipInfo.chipName.c_str());
delete[] response; // Cleanup before return delete[] response; // Cleanup before return
return chipInfo.driver; return chipInfo.driver;
@@ -1486,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') { 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 // Reset the response buffer for the next potential message
responseLen = 0; responseLen = 0;
response[0] = '\0'; response[0] = '\0';
@@ -1504,10 +1527,7 @@ GPS *GPS::createGps()
int8_t _rx_gpio = config.position.rx_gpio; int8_t _rx_gpio = config.position.rx_gpio;
int8_t _tx_gpio = config.position.tx_gpio; int8_t _tx_gpio = config.position.tx_gpio;
int8_t _en_gpio = config.position.gps_en_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 defined(GPS_RX_PIN)
if (!_rx_gpio) if (!_rx_gpio)
_rx_gpio = GPS_RX_PIN; _rx_gpio = GPS_RX_PIN;
@@ -1572,8 +1592,6 @@ GPS *GPS::createGps()
#ifdef PIN_GPS_RESET #ifdef PIN_GPS_RESET
pinMode(PIN_GPS_RESET, OUTPUT); pinMode(PIN_GPS_RESET, OUTPUT);
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
delay(10);
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE); digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
#endif #endif
@@ -1583,16 +1601,28 @@ GPS *GPS::createGps()
_serial_gps->setRxBufferSize(SERIAL_BUFFER_SIZE); // the default is 256 _serial_gps->setRxBufferSize(SERIAL_BUFFER_SIZE); // the default is 256
#endif #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 RX", new_gps->rx_gpio);
LOG_DEBUG("Use GPIO%d for GPS TX", new_gps->tx_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); _serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, new_gps->rx_gpio, new_gps->tx_gpio);
#elif defined(ARCH_RP2040) #elif defined(ARCH_RP2040)
_serial_gps->setPinout(new_gps->tx_gpio, new_gps->rx_gpio);
_serial_gps->setFIFOSize(256); _serial_gps->setFIFOSize(256);
_serial_gps->begin(GPS_BAUDRATE); _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); _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 #endif
} }
return new_gps; return new_gps;
@@ -1659,8 +1689,12 @@ bool GPS::lookForLocation()
#ifndef TINYGPS_OPTION_NO_STATISTICS #ifndef TINYGPS_OPTION_NO_STATISTICS
if (reader.failedChecksum() > lastChecksumFailCount) { if (reader.failedChecksum() > lastChecksumFailCount) {
LOG_WARN("%u new GPS checksum failures, for a total of %u", reader.failedChecksum() - lastChecksumFailCount, // In a GPS_DEBUG build we want to log all of these. In production, we only care if there are many of them.
reader.failedChecksum()); #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(); lastChecksumFailCount = reader.failedChecksum();
} }
#endif #endif

View File

@@ -194,6 +194,8 @@ class GPS : private concurrency::OSThread
/** If !NULL we will use this serial port to construct our GPS */ /** If !NULL we will use this serial port to construct our GPS */
#if defined(ARCH_RP2040) #if defined(ARCH_RP2040)
static SerialUART *_serial_gps; static SerialUART *_serial_gps;
#elif defined(ARCH_NRF52)
static Uart *_serial_gps;
#else #else
static HardwareSerial *_serial_gps; static HardwareSerial *_serial_gps;
#endif #endif

View File

@@ -112,7 +112,11 @@ RTCSetResult readFromRTC()
#elif defined(RX8130CE_RTC) #elif defined(RX8130CE_RTC)
if (rtc_found.address == RX8130CE_RTC) { if (rtc_found.address == RX8130CE_RTC) {
uint32_t now = millis(); uint32_t now = millis();
#ifdef MUZI_BASE
ArtronShop_RX8130CE rtc(&Wire1);
#else
ArtronShop_RX8130CE rtc(&Wire); ArtronShop_RX8130CE rtc(&Wire);
#endif
tm t; tm t;
if (rtc.getTime(&t)) { if (rtc.getTime(&t)) {
tv.tv_sec = gm_mktime(&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) #elif defined(RX8130CE_RTC)
if (rtc_found.address == RX8130CE_RTC) { if (rtc_found.address == RX8130CE_RTC) {
#ifdef MUZI_BASE
ArtronShop_RX8130CE rtc(&Wire1);
#else
ArtronShop_RX8130CE rtc(&Wire); ArtronShop_RX8130CE rtc(&Wire);
#endif
tm *t = gmtime(&tv->tv_sec); tm *t = gmtime(&tv->tv_sec);
if (rtc.setTime(*t)) { if (rtc.setTime(*t)) {
LOG_DEBUG("RX8130CE setDateTime %02d-%02d-%02d %02d:%02d:%02d (%ld)", t->tm_year + 1900, t->tm_mon + 1, 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 #ifdef BUILD_EPOCH
if (tv.tv_sec < BUILD_EPOCH) { if (tv.tv_sec < BUILD_EPOCH) {
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) { 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(); lastTimeValidationWarning = millis();
} }
return RTCSetResultInvalidTime; return RTCSetResultInvalidTime;
@@ -319,7 +327,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
// Calculate max allowed time safely to avoid overflow in logging // Calculate max allowed time safely to avoid overflow in logging
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS; uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime; 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); (uint32_t)BUILD_EPOCH, maxAllowedPrintable);
lastTimeValidationWarning = millis(); lastTimeValidationWarning = millis();
} }

View File

@@ -324,7 +324,7 @@ static int8_t prevFrame = -1;
// Combined dynamic node list frame cycling through LastHeard, HopSignal, and Distance modes // 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) // 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); SPIClass SPI1(HSPI);
#endif #endif
@@ -356,7 +356,13 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#else #else
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT); dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
#endif #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) #elif defined(USE_SSD1306)
dispdev = new SSD1306Wire(address.address, -1, -1, geometry, dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
@@ -369,7 +375,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
LOG_INFO("SSD1306 init success"); LOG_INFO("SSD1306 init success");
} }
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7789_CS) || \ #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, dispdev = new TFTDisplay(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY) #elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
@@ -399,6 +405,12 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
isAUTOOled = true; isAUTOOled = true;
#endif #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); ui = new OLEDDisplayUi(dispdev);
cmdQueue.setReader(this); cmdQueue.setReader(this);
} }
@@ -435,6 +447,14 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
PMU->enablePowerOutput(XPOWERS_ALDO2); PMU->enablePowerOutput(XPOWERS_ALDO2);
#endif #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 #if !ARCH_PORTDUINO
dispdev->displayOn(); dispdev->displayOn();
#endif #endif
@@ -443,7 +463,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
if (uiconfig.screen_brightness == 1) if (uiconfig.screen_brightness == 1)
digitalWrite(PIN_EINK_EN, HIGH); digitalWrite(PIN_EINK_EN, HIGH);
#elif defined(PCA_PIN_EINK_EN) #elif defined(PCA_PIN_EINK_EN)
if (uiconfig.screen_brightness == 1) if (uiconfig.screen_brightness > 0)
io.digitalWrite(PCA_PIN_EINK_EN, HIGH); io.digitalWrite(PCA_PIN_EINK_EN, HIGH);
#endif #endif
@@ -466,6 +486,15 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
pinMode(VTFT_LEDA, OUTPUT); pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON); digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
#endif #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 #endif
enabled = true; enabled = true;
setInterval(0); // Draw ASAP setInterval(0); // Draw ASAP
@@ -484,6 +513,10 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
#endif #endif
dispdev->displayOff(); dispdev->displayOff();
#ifdef SCREEN_12V_ENABLE
digitalWrite(SCREEN_12V_ENABLE, LOW);
#endif
#ifdef USE_ST7789 #ifdef USE_ST7789
SPI1.end(); SPI1.end();
#if defined(ARCH_ESP32) #if defined(ARCH_ESP32)
@@ -500,6 +533,21 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
nrf_gpio_cfg_default(ST7789_NSS); nrf_gpio_cfg_default(ST7789_NSS);
#endif #endif
#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 #ifdef T_WATCH_S3
PMU->disablePowerOutput(XPOWERS_ALDO2); PMU->disablePowerOutput(XPOWERS_ALDO2);
@@ -534,7 +582,7 @@ void Screen::setup()
static_cast<AutoOLEDWire *>(dispdev)->setDetected(model); static_cast<AutoOLEDWire *>(dispdev)->setDetected(model);
#endif #endif
#ifdef USE_SH1107_128_64 #if defined(USE_SH1107_128_64) || defined(USE_SH1107)
static_cast<SH1106Wire *>(dispdev)->setSubtype(7); static_cast<SH1106Wire *>(dispdev)->setSubtype(7);
#endif #endif
@@ -542,6 +590,13 @@ void Screen::setup()
// Apply custom RGB color (e.g. Heltec T114/T190) // Apply custom RGB color (e.g. Heltec T114/T190)
static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH); static_cast<ST7789Spi *>(dispdev)->setRGB(TFT_MESH);
#endif #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 === // === Initialize display and UI system ===
ui->init(); ui->init();
@@ -601,10 +656,12 @@ void Screen::setup()
#else #else
if (!config.display.flip_screen) { if (!config.display.flip_screen) {
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7789_CS) || \ #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(); static_cast<TFTDisplay *>(dispdev)->flipScreenVertically();
#elif defined(USE_ST7789) #elif defined(USE_ST7789)
static_cast<ST7789Spi *>(dispdev)->flipScreenVertically(); static_cast<ST7789Spi *>(dispdev)->flipScreenVertically();
#elif defined(USE_ST7796)
static_cast<ST7796Spi *>(dispdev)->mirrorScreen();
#elif !defined(M5STACK_UNITC6L) #elif !defined(M5STACK_UNITC6L)
dispdev->flipScreenVertically(); dispdev->flipScreenVertically();
#endif #endif
@@ -637,7 +694,7 @@ void Screen::setup()
touchScreenImpl1->init(); touchScreenImpl1->init();
} }
} }
#elif HAS_TOUCHSCREEN && !defined(USE_EINK) #elif HAS_TOUCHSCREEN && !defined(USE_EINK) && !HAS_CST226SE
touchScreenImpl1 = touchScreenImpl1 =
new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch); new TouchScreenImpl1(dispdev->getWidth(), dispdev->getHeight(), static_cast<TFTDisplay *>(dispdev)->getTouch);
touchScreenImpl1->init(); touchScreenImpl1->init();
@@ -1549,6 +1606,7 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
int Screen::handleInputEvent(const InputEvent *event) int Screen::handleInputEvent(const InputEvent *event)
{ {
LOG_INPUT("Screen Input event %u! kb %u", event->inputEvent, event->kbchar);
if (!screenOn) if (!screenOn)
return 0; return 0;

View File

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

View File

@@ -73,7 +73,8 @@
#endif #endif
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ #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) !defined(DISPLAY_FORCE_SMALL_FONTS)
// The screen is bigger so use bigger fonts // The screen is bigger so use bigger fonts
#define FONT_SMALL FONT_MEDIUM_LOCAL // Height: 19 #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 "RTC.h"
#include "draw/NodeListRenderer.h"
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
#include "graphics/draw/UIRenderer.h" #include "graphics/draw/UIRenderer.h"
#include "main.h" #include "main.h"
#include "meshtastic/config.pb.h" #include "meshtastic/config.pb.h"
@@ -13,6 +17,12 @@ namespace graphics
void determineResolution(int16_t screenheight, int16_t screenwidth) void determineResolution(int16_t screenheight, int16_t screenwidth)
{ {
#ifdef FORCE_LOW_RES
isHighResolution = false;
return;
#endif
if (screenwidth > 128) { if (screenwidth > 128) {
isHighResolution = true; isHighResolution = true;
} }
@@ -20,11 +30,6 @@ void determineResolution(int16_t screenheight, int16_t screenwidth)
if (screenwidth > 128 && screenheight <= 64) { if (screenwidth > 128 && screenheight <= 64) {
isHighResolution = false; isHighResolution = false;
} }
// Special case for Heltec Wireless Tracker v1.1
if (screenwidth == 160 && screenheight == 80) {
isHighResolution = false;
}
} }
// === Shared External State === // === Shared External State ===
@@ -396,6 +401,43 @@ const int *getTextPositions(OLEDDisplay *display)
return textPositions; 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) bool isAllowedPunctuation(char c)
{ {
const std::string allowed = ".,!?;:-_()[]{}'\"@#$/\\&+=%~^ "; const std::string allowed = ".,!?;:-_()[]{}'\"@#$/\\&+=%~^ ";
@@ -423,3 +465,4 @@ std::string sanitizeString(const std::string &input)
} }
} // namespace graphics } // 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, void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr = "", bool force_no_invert = false,
bool show_date = 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); const int *getTextPositions(OLEDDisplay *display);
bool isAllowedPunctuation(char c); bool isAllowedPunctuation(char c);

View File

@@ -123,6 +123,11 @@ static void rak14014_tpIntHandle(void)
_rak14014_touch_int = true; _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) #elif defined(ST72xx_DE)
#include <LovyanGFX.hpp> #include <LovyanGFX.hpp>
#include <TCA9534.h> #include <TCA9534.h>
@@ -422,7 +427,57 @@ static LGFX *tft = nullptr;
#elif defined(ST7789_CS) #elif defined(ST7789_CS)
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip #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 class LGFX : public lgfx::LGFX_Device
{ {
lgfx::Panel_ST7789 _panel_instance; lgfx::Panel_ST7789 _panel_instance;
@@ -431,6 +486,8 @@ class LGFX : public lgfx::LGFX_Device
#if HAS_TOUCHSCREEN #if HAS_TOUCHSCREEN
#if defined(T_WATCH_S3) || defined(ELECROW) #if defined(T_WATCH_S3) || defined(ELECROW)
lgfx::Touch_FT5x06 _touch_instance; lgfx::Touch_FT5x06 _touch_instance;
#elif defined(HELTEC_V4_TFT)
lgfx::TOUCH_CHSC6X _touch_instance;
#else #else
lgfx::Touch_GT911 _touch_instance; lgfx::Touch_GT911 _touch_instance;
#endif #endif
@@ -464,9 +521,9 @@ class LGFX : public lgfx::LGFX_Device
{ // Set the display panel control. { // Set the display panel control.
auto cfg = _panel_instance.config(); // Gets a structure for display panel settings. 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_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_rst = ST7789_RESET; // Pin number where RST is connected (-1 = disable)
cfg.pin_busy = -1; // Pin number where BUSY 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 // The following setting values are general initial values for each panel, so please comment out any
// unknown items and try them. // unknown items and try them.
@@ -1083,7 +1140,7 @@ static LGFX *tft = nullptr;
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(ST7789_CS) || defined(ST7796_CS) || defined(ILI9341_DRIVER) || \ #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) || \ defined(ILI9342_DRIVER) || defined(RAK14014) || defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST72xx_DE) || \
(ARCH_PORTDUINO && HAS_SCREEN != 0) (ARCH_PORTDUINO && HAS_SCREEN != 0) || defined(HACKADAY_COMMUNICATOR)
#include "SPILock.h" #include "SPILock.h"
#include "TFTDisplay.h" #include "TFTDisplay.h"
#include <SPI.h> #include <SPI.h>
@@ -1219,12 +1276,15 @@ void TFTDisplay::display(bool fromBlank)
x_LastPixelUpdate = x; 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. // 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. // 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, tft->pushRect(x_FirstPixelUpdate, y, (x_LastPixelUpdate - x_FirstPixelUpdate + 1), 1,
&linePixelBuffer[x_FirstPixelUpdate]); &linePixelBuffer[x_FirstPixelUpdate]);
#endif
somethingChanged = true; somethingChanged = true;
} }
y++; y++;
@@ -1288,6 +1348,8 @@ void TFTDisplay::sendCommand(uint8_t com)
display(true); display(true);
if (portduino_config.displayBacklight.pin > 0) if (portduino_config.displayBacklight.pin > 0)
digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON); digitalWrite(portduino_config.displayBacklight.pin, TFT_BACKLIGHT_ON);
#elif defined(HACKADAY_COMMUNICATOR)
tft->displayOn();
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
tft->wakeup(); tft->wakeup();
tft->powerSaveOff(); tft->powerSaveOff();
@@ -1300,7 +1362,8 @@ void TFTDisplay::sendCommand(uint8_t com)
unphone.backlight(true); // using unPhone library unphone.backlight(true); // using unPhone library
#endif #endif
#ifdef RAK14014 #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); tft->setBrightness(172);
#endif #endif
break; break;
@@ -1312,6 +1375,8 @@ void TFTDisplay::sendCommand(uint8_t com)
tft->clear(); tft->clear();
if (portduino_config.displayBacklight.pin > 0) if (portduino_config.displayBacklight.pin > 0)
digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON); digitalWrite(portduino_config.displayBacklight.pin, !TFT_BACKLIGHT_ON);
#elif defined(HACKADAY_COMMUNICATOR)
tft->displayOff();
#elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE) #elif !defined(RAK14014) && !defined(M5STACK) && !defined(UNPHONE)
tft->sleep(); tft->sleep();
tft->powerSaveOn(); tft->powerSaveOn();
@@ -1324,7 +1389,7 @@ void TFTDisplay::sendCommand(uint8_t com)
unphone.backlight(false); // using unPhone library unphone.backlight(false); // using unPhone library
#endif #endif
#ifdef RAK14014 #ifdef RAK14014
#elif !defined(M5STACK) #elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
tft->setBrightness(0); tft->setBrightness(0);
#endif #endif
break; break;
@@ -1340,7 +1405,7 @@ void TFTDisplay::setDisplayBrightness(uint8_t _brightness)
{ {
#ifdef RAK14014 #ifdef RAK14014
// todo // todo
#else #elif !defined(HACKADAY_COMMUNICATOR)
tft->setBrightness(_brightness); tft->setBrightness(_brightness);
LOG_DEBUG("Brightness is set to value: %i ", _brightness); LOG_DEBUG("Brightness is set to value: %i ", _brightness);
#endif #endif
@@ -1358,7 +1423,7 @@ bool TFTDisplay::hasTouch(void)
{ {
#ifdef RAK14014 #ifdef RAK14014
return true; return true;
#elif !defined(M5STACK) #elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
return tft->touch() != nullptr; return tft->touch() != nullptr;
#else #else
return false; return false;
@@ -1377,7 +1442,7 @@ bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
} else { } else {
return false; return false;
} }
#elif !defined(M5STACK) #elif !defined(M5STACK) && !defined(HACKADAY_COMMUNICATOR)
return tft->getTouch(x, y); return tft->getTouch(x, y);
#else #else
return false; return false;
@@ -1396,6 +1461,12 @@ bool TFTDisplay::connect()
LOG_INFO("Do TFT init"); LOG_INFO("Do TFT init");
#ifdef RAK14014 #ifdef RAK14014
tft = new TFT_eSPI; 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 #else
tft = new LGFX; tft = new LGFX;
#endif #endif
@@ -1406,8 +1477,15 @@ bool TFTDisplay::connect()
#ifdef UNPHONE #ifdef UNPHONE
unphone.backlight(true); // using unPhone library unphone.backlight(true); // using unPhone library
#endif #endif
#ifdef HACKADAY_COMMUNICATOR
bool beginStatus = tft->begin();
if (beginStatus)
LOG_DEBUG("TFT Success!");
else
LOG_ERROR("TFT Fail!");
#else
tft->init(); tft->init();
#endif
#if defined(M5STACK) #if defined(M5STACK)
tft->setRotation(0); tft->setRotation(0);

View File

@@ -101,3 +101,23 @@ void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
else else
snprintf(timeStr, maxLength, "unknown age"); 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 * @param maxLength Maximum length of the resulting string buffer
*/ */
void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength); 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" #include "configuration.h"
#if HAS_SCREEN
#include "VirtualKeyboard.h"
#include "graphics/Screen.h" #include "graphics/Screen.h"
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h" #include "graphics/SharedUIDisplay.h"
@@ -736,3 +737,4 @@ bool VirtualKeyboard::isTimedOut() const
} }
} // namespace graphics } // namespace graphics
#endif

View File

@@ -194,17 +194,12 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
graphics::drawCommonHeader(display, x, y, titleStr, true, true); graphics::drawCommonHeader(display, x, y, titleStr, true, true);
int line = 0; 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 uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
char timeString[16]; char timeString[16];
int hour = 0; int hour = 0;
int minute = 0; int minute = 0;
int second = 0; int second = 0;
if (rtc_sec > 0) { if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY; long hms = rtc_sec % SEC_PER_DAY;
hms = (hms + SEC_PER_DAY) % 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; bool isPM = hour >= 12;
// hour = hour > 12 ? hour - 12 : hour;
if (config.display.use_12h_clock) { if (config.display.use_12h_clock) {
hour %= 12; hour %= 12;
if (hour == 0) if (hour == 0) {
hour = 12; hour = 12;
}
snprintf(timeString, sizeof(timeString), "%d:%02d", hour, minute); snprintf(timeString, sizeof(timeString), "%d:%02d", hour, minute);
} else { } else {
snprintf(timeString, sizeof(timeString), "%02d:%02d", hour, minute); snprintf(timeString, sizeof(timeString), "%02d:%02d", hour, minute);
@@ -229,24 +224,56 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
char secondString[8]; char secondString[8];
snprintf(secondString, sizeof(secondString), "%02d", second); snprintf(secondString, sizeof(secondString), "%02d", second);
#ifdef T_WATCH_S3 static bool scaleInitialized = false;
float scale = 1.5; static float scale = 0.75f;
#elif defined(CHATTER_2) static float segmentWidth = SEGMENT_WIDTH * 0.75f;
float scale = 1.1; static float segmentHeight = SEGMENT_HEIGHT * 0.75f;
#else
float scale = 0.75;
if (isHighResolution) {
scale = 1.5;
}
#endif
uint16_t segmentWidth = SEGMENT_WIDTH * scale; if (!scaleInitialized) {
uint16_t segmentHeight = SEGMENT_HEIGHT * scale; 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 // 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]; char character = timeString[i];
if (character == ':') { if (character == ':') {
@@ -257,19 +284,21 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
} }
uint16_t hourMinuteTextX = (display->getWidth() / 2) - (timeStringWidth / 2); uint16_t hourMinuteTextX = (display->getWidth() / 2) - (timeStringWidth / 2);
uint16_t startingHourMinuteTextX = hourMinuteTextX; 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 // 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]; char character = timeString[i];
if (character == ':') { if (character == ':') {
drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale); drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale);
hourMinuteTextX += segmentHeight + 6; hourMinuteTextX += segmentHeight + 6;
if (scale >= 2.0f) {
hourMinuteTextX += (uint16_t)(4.5f * scale);
}
} else { } else {
drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character - '0', scale); drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character - '0', scale);
@@ -279,34 +308,27 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
hourMinuteTextX += 5; hourMinuteTextX += 5;
} }
// draw seconds string // draw seconds string + AM/PM
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
int xOffset = (isHighResolution) ? 0 : -1; int xOffset = (isHighResolution) ? 0 : -1;
if (hour >= 10) { if (hour >= 10) {
xOffset += (isHighResolution) ? 32 : 18; 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) { if (config.display.use_12h_clock) {
display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2, display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - 1, isPM ? "pm" : "am");
isPM ? "pm" : "am");
} }
#ifndef USE_EINK #ifndef USE_EINK
xOffset = (isHighResolution) ? 18 : 10; 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); secondString);
#endif #endif
}
void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y) graphics::drawCommonFooter(display, x, y);
{
display->drawFastImage(x, y, 18, 14, bluetoothConnectedIcon);
} }
// Draw an analog clock // Draw an analog clock
@@ -319,11 +341,6 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
graphics::drawCommonHeader(display, x, y, titleStr, true, true); graphics::drawCommonHeader(display, x, y, titleStr, true, true);
int line = 0; int line = 0;
#ifdef T_WATCH_S3
if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
}
#endif
// clock face center coordinates // clock face center coordinates
int16_t centerX = display->getWidth() / 2; int16_t centerX = display->getWidth() / 2;
int16_t centerY = display->getHeight() / 2; int16_t centerY = display->getHeight() / 2;
@@ -516,6 +533,7 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
display->drawLine(centerX, centerY, secondX, secondY); display->drawLine(centerX, centerY, secondX, secondY);
#endif #endif
} }
graphics::drawCommonFooter(display, x, y);
} }
} // namespace ClockRenderer } // 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 // UI elements for clock displays
// void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode = true, float scale = 1); // 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 } // namespace ClockRenderer

View File

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

View File

@@ -3,6 +3,7 @@
#include "../Screen.h" #include "../Screen.h"
#include "DebugRenderer.h" #include "DebugRenderer.h"
#include "FSCommon.h" #include "FSCommon.h"
#include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "Throttle.h" #include "Throttle.h"
#include "UIRenderer.h" #include "UIRenderer.h"
@@ -10,6 +11,7 @@
#include "gps/RTC.h" #include "gps/RTC.h"
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h" #include "graphics/SharedUIDisplay.h"
#include "graphics/TimeFormatters.h"
#include "graphics/images.h" #include "graphics/images.h"
#include "main.h" #include "main.h"
#include "mesh/Channels.h" #include "mesh/Channels.h"
@@ -95,7 +97,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
(storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit (storeForwardModule->heartbeatInterval * 1200))) { // no heartbeat, overlap a bit
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \ defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
ARCH_PORTDUINO) && \ defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796) || ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS) !defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12,
8, imgQuestionL1); 8, imgQuestionL1);
@@ -107,7 +109,8 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
#endif #endif
} else { } else {
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \ defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS) !defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 16, display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 16,
8, imgSFL1); 8, imgSFL1);
@@ -123,7 +126,7 @@ void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16
// TODO: Raspberry Pi supports more than just the one screen size // TODO: Raspberry Pi supports more than just the one screen size
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \ defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
ARCH_PORTDUINO) && \ defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796) || ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS) !defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8, display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(screen->ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
imgInfoL1); imgInfoL1);
@@ -223,6 +226,8 @@ void drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, i
display->drawString(x, getTextPositions(display)[line++], "URL: http://meshtastic.local"); display->drawString(x, getTextPositions(display)[line++], "URL: http://meshtastic.local");
graphics::drawCommonFooter(display, x, y);
/* Display a heartbeat pixel that blinks every time the frame is redrawn */ /* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS #ifdef SHOW_REDRAWS
if (heartbeat) if (heartbeat)
@@ -503,6 +508,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
display->drawString(starting_position + chUtil_x + chutil_bar_width + extraoffset, getTextPositions(display)[line++], display->drawString(starting_position + chUtil_x + chutil_bar_width + extraoffset, getTextPositions(display)[line++],
chUtilPercentage); chUtilPercentage);
#endif #endif
graphics::drawCommonFooter(display, x, y);
} }
// **************************** // ****************************
@@ -642,27 +648,48 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
int textWidth = display->getStringWidth(appversionstr); int textWidth = display->getStringWidth(appversionstr);
int nameX = (SCREEN_WIDTH - textWidth) / 2; int nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line], appversionstr); display->drawString(nameX, getTextPositions(display)[line++], appversionstr);
#if !defined(M5STACK_UNITC6L)
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line < 4)) { // Only show uptime if the screen can show it if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line <= 5)) { // Only show uptime if the screen can show it
line += 1;
char uptimeStr[32] = ""; char uptimeStr[32] = "";
uint32_t uptime = millis() / 1000; getUptimeStr(millis(), "Up", uptimeStr, sizeof(uptimeStr));
uint32_t days = uptime / 86400;
uint32_t hours = (uptime % 86400) / 3600;
uint32_t mins = (uptime % 3600) / 60;
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
if (days)
snprintf(uptimeStr, sizeof(uptimeStr), " Up: %ud %uh", days, hours);
else if (hours)
snprintf(uptimeStr, sizeof(uptimeStr), " Up: %uh %um", hours, mins);
else
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
textWidth = display->getStringWidth(uptimeStr); textWidth = display->getStringWidth(uptimeStr);
nameX = (SCREEN_WIDTH - textWidth) / 2; nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line], uptimeStr); display->drawString(nameX, getTextPositions(display)[line++], uptimeStr);
} }
#endif
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line <= 5)) { // Only show API state if the screen can show it
char api_state[32] = "";
const char *clientWord = nullptr;
// Determine if narrow or wide screen
if (isHighResolution) {
clientWord = "Client";
} else {
clientWord = "App";
}
snprintf(api_state, sizeof(api_state), "No %ss Connected", clientWord);
if (service->api_state == service->STATE_BLE) {
snprintf(api_state, sizeof(api_state), "%s Connected (BLE)", clientWord);
} else if (service->api_state == service->STATE_WIFI) {
snprintf(api_state, sizeof(api_state), "%s Connected (WiFi)", clientWord);
} else if (service->api_state == service->STATE_SERIAL) {
snprintf(api_state, sizeof(api_state), "%s Connected (Serial)", clientWord);
} else if (service->api_state == service->STATE_PACKET) {
snprintf(api_state, sizeof(api_state), "%s Connected (Internal)", clientWord);
} else if (service->api_state == service->STATE_HTTP) {
snprintf(api_state, sizeof(api_state), "%s Connected (HTTP)", clientWord);
} else if (service->api_state == service->STATE_ETH) {
snprintf(api_state, sizeof(api_state), "%s Connected (Ethernet)", clientWord);
}
if (api_state[0] != '\0') {
display->drawString((SCREEN_WIDTH - display->getStringWidth(api_state)) / 2, getTextPositions(display)[line++],
api_state);
}
}
graphics::drawCommonFooter(display, x, y);
} }
// **************************** // ****************************
@@ -694,4 +721,4 @@ void drawChirpy(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int1
} // namespace DebugRenderer } // namespace DebugRenderer
} // namespace graphics } // namespace graphics
#endif #endif

View File

@@ -119,26 +119,8 @@ void menuHandler::LoraRegionPicker(uint32_t duration)
auto changes = SEGMENT_CONFIG; auto changes = SEGMENT_CONFIG;
// This is needed as we wait til picking the LoRa region to generate keys for the first time. // This is needed as we wait til picking the LoRa region to generate keys for the first time.
if (!owner.is_licensed) { // Use consolidated key generation function
bool keygenSuccess = false; nodeDB->generateCryptoKeyPair();
if (config.security.private_key.size == 32) {
// public key is derived from private, so this will always have the same result.
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
keygenSuccess = true;
}
} else {
LOG_INFO("Generate new PKI keys");
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
keygenSuccess = true;
}
if (keygenSuccess) {
config.security.public_key.size = 32;
config.security.private_key.size = 32;
owner.public_key.size = 32;
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
}
}
config.lora.tx_enabled = true; config.lora.tx_enabled = true;
initRegion(); initRegion();
if (myRegion->dutyCycle < 100) { if (myRegion->dutyCycle < 100) {
@@ -515,7 +497,7 @@ void menuHandler::homeBaseMenu()
} }
saveUIConfig(); saveUIConfig();
#elif defined(PCA_PIN_EINK_EN) #elif defined(PCA_PIN_EINK_EN)
if (uiconfig.screen_brightness == 1) { if (uiconfig.screen_brightness > 0) {
uiconfig.screen_brightness = 0; uiconfig.screen_brightness = 0;
io.digitalWrite(PCA_PIN_EINK_EN, LOW); io.digitalWrite(PCA_PIN_EINK_EN, LOW);
} else { } else {
@@ -574,27 +556,26 @@ void menuHandler::textMessageBaseMenu()
void menuHandler::systemBaseMenu() void menuHandler::systemBaseMenu()
{ {
enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, PowerMenu, FrameToggles, Test, enumEnd }; enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, WiFiToggle, PowerMenu, Test, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"}; static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[enumEnd] = {Back}; static int optionsEnumArray[enumEnd] = {Back};
int options = 1; int options = 1;
optionsArray[options] = "Notifications"; optionsArray[options] = "Notifications";
optionsEnumArray[options++] = Notifications; optionsEnumArray[options++] = Notifications;
#if defined(ST7789_CS) || defined(ST7796_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || \ optionsArray[options] = "Display Options";
defined(USE_SH1107) || defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
optionsArray[options] = "Screen Options";
optionsEnumArray[options++] = ScreenOptions; optionsEnumArray[options++] = ScreenOptions;
#endif
optionsArray[options] = "Frame Visiblity Toggle";
optionsEnumArray[options++] = FrameToggles;
#if defined(M5STACK_UNITC6L) #if defined(M5STACK_UNITC6L)
optionsArray[options] = "Bluetooth"; optionsArray[options] = "Bluetooth";
#else #else
optionsArray[options] = "Bluetooth Toggle"; optionsArray[options] = "Bluetooth Toggle";
#endif #endif
optionsEnumArray[options++] = Bluetooth; optionsEnumArray[options++] = Bluetooth;
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
optionsArray[options] = "WiFi Toggle";
optionsEnumArray[options++] = WiFiToggle;
#endif
#if defined(M5STACK_UNITC6L) #if defined(M5STACK_UNITC6L)
optionsArray[options] = "Power"; optionsArray[options] = "Power";
#else #else
@@ -626,15 +607,17 @@ void menuHandler::systemBaseMenu()
} else if (selected == PowerMenu) { } else if (selected == PowerMenu) {
menuHandler::menuQueue = menuHandler::power_menu; menuHandler::menuQueue = menuHandler::power_menu;
screen->runNow(); screen->runNow();
} else if (selected == FrameToggles) {
menuHandler::menuQueue = menuHandler::FrameToggles;
screen->runNow();
} else if (selected == Test) { } else if (selected == Test) {
menuHandler::menuQueue = menuHandler::test_menu; menuHandler::menuQueue = menuHandler::test_menu;
screen->runNow(); screen->runNow();
} else if (selected == Bluetooth) { } else if (selected == Bluetooth) {
menuQueue = bluetooth_toggle_menu; menuQueue = bluetooth_toggle_menu;
screen->runNow(); screen->runNow();
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
} else if (selected == WiFiToggle) {
menuQueue = wifi_toggle_menu;
screen->runNow();
#endif
} else if (selected == Back && !test_enabled) { } else if (selected == Back && !test_enabled) {
test_count++; test_count++;
if (test_count > 4) { if (test_count > 4) {
@@ -784,22 +767,30 @@ void menuHandler::nodeNameLengthMenu()
screen->runNow(); screen->runNow();
} }
}; };
bannerOptions.InitialSelected = config.display.use_long_node_name == true ? 1 : 2;
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::resetNodeDBMenu() void menuHandler::resetNodeDBMenu()
{ {
static const char *optionsArray[] = {"Back", "Confirm"}; static const char *optionsArray[] = {"Back", "Reset All", "Preserve Favorites"};
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Confirm Reset NodeDB"; bannerOptions.message = "Confirm Reset NodeDB";
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 2; bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1) { if (selected == 1 || selected == 2) {
disableBluetooth(); disableBluetooth();
screen->setFrames(Screen::FOCUS_DEFAULT);
}
if (selected == 1) {
LOG_INFO("Initiate node-db reset"); LOG_INFO("Initiate node-db reset");
nodeDB->resetNodes(); nodeDB->resetNodes();
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
} else if (selected == 2) {
LOG_INFO("Initiate node-db reset but keeping favorites");
nodeDB->resetNodes(1);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
} }
}; };
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
@@ -935,7 +926,9 @@ void menuHandler::BluetoothToggleMenu()
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3; bannerOptions.optionsCount = 3;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == 1 || selected == 2) { if (selected == 0)
return;
else if (selected != (config.bluetooth.enabled ? 1 : 2)) {
InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0}; InputEvent event = {.inputEvent = (input_broker_event)170, .kbchar = 170, .touchX = 0, .touchY = 0};
inputBroker->injectInputEvent(&event); inputBroker->injectInputEvent(&event);
} }
@@ -1034,7 +1027,8 @@ void menuHandler::TFTColorPickerMenu(OLEDDisplay *display)
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 10; bannerOptions.optionsCount = 10;
bannerOptions.bannerCallback = [display](int selected) -> void { bannerOptions.bannerCallback = [display](int selected) -> void {
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || HAS_TFT #if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \
HAS_TFT || defined(HACKADAY_COMMUNICATOR)
uint8_t TFT_MESH_r = 0; uint8_t TFT_MESH_r = 0;
uint8_t TFT_MESH_g = 0; uint8_t TFT_MESH_g = 0;
uint8_t TFT_MESH_b = 0; uint8_t TFT_MESH_b = 0;
@@ -1274,19 +1268,28 @@ void menuHandler::wifiBaseMenu()
void menuHandler::wifiToggleMenu() void menuHandler::wifiToggleMenu()
{ {
enum optionsNumbers { Back, Wifi_toggle }; enum optionsNumbers { Back, Wifi_disable, Wifi_enable };
static const char *optionsArray[] = {"Back", "Disable"}; static const char *optionsArray[] = {"Back", "WiFi Disabled", "WiFi Enabled"};
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Disable Wifi and\nEnable Bluetooth?"; bannerOptions.message = "WiFi Actions";
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 2; bannerOptions.optionsCount = 3;
if (config.network.wifi_enabled == true)
bannerOptions.InitialSelected = 2;
else
bannerOptions.InitialSelected = 1;
bannerOptions.bannerCallback = [](int selected) -> void { bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == Wifi_toggle) { if (selected == Wifi_disable) {
config.network.wifi_enabled = false; config.network.wifi_enabled = false;
config.bluetooth.enabled = true; config.bluetooth.enabled = true;
service->reloadConfig(SEGMENT_CONFIG); service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000); rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
} else if (selected == Wifi_enable) {
config.network.wifi_enabled = true;
config.bluetooth.enabled = false;
service->reloadConfig(SEGMENT_CONFIG);
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
} }
}; };
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
@@ -1329,12 +1332,12 @@ void menuHandler::screenOptionsMenu()
hasSupportBrightness = false; hasSupportBrightness = false;
#endif #endif
enum optionsNumbers { Back, NodeNameLength, Brightness, ScreenColor }; enum optionsNumbers { Back, NodeNameLength, Brightness, ScreenColor, FrameToggles, DisplayUnits };
static const char *optionsArray[5] = {"Back"}; static const char *optionsArray[5] = {"Back"};
static int optionsEnumArray[5] = {Back}; static int optionsEnumArray[5] = {Back};
int options = 1; int options = 1;
#if defined(T_DECK) || defined(T_LORA_PAGER) #if defined(T_DECK) || defined(T_LORA_PAGER) || defined(HACKADAY_COMMUNICATOR)
optionsArray[options] = "Show Long/Short Name"; optionsArray[options] = "Show Long/Short Name";
optionsEnumArray[options++] = NodeNameLength; optionsEnumArray[options++] = NodeNameLength;
#endif #endif
@@ -1346,13 +1349,20 @@ void menuHandler::screenOptionsMenu()
} }
// Only show screen color for TFT displays // Only show screen color for TFT displays
#if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || HAS_TFT #if defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || defined(T_DECK) || defined(T_LORA_PAGER) || \
HAS_TFT || defined(HACKADAY_COMMUNICATOR)
optionsArray[options] = "Screen Color"; optionsArray[options] = "Screen Color";
optionsEnumArray[options++] = ScreenColor; optionsEnumArray[options++] = ScreenColor;
#endif #endif
optionsArray[options] = "Frame Visibility Toggle";
optionsEnumArray[options++] = FrameToggles;
optionsArray[options] = "Display Units";
optionsEnumArray[options++] = DisplayUnits;
BannerOverlayOptions bannerOptions; BannerOverlayOptions bannerOptions;
bannerOptions.message = "Screen Options"; bannerOptions.message = "Display Options";
bannerOptions.optionsArrayPtr = optionsArray; bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = options; bannerOptions.optionsCount = options;
bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.optionsEnumPtr = optionsEnumArray;
@@ -1366,6 +1376,12 @@ void menuHandler::screenOptionsMenu()
} else if (selected == NodeNameLength) { } else if (selected == NodeNameLength) {
menuHandler::menuQueue = menuHandler::node_name_length_menu; menuHandler::menuQueue = menuHandler::node_name_length_menu;
screen->runNow(); screen->runNow();
} else if (selected == FrameToggles) {
menuHandler::menuQueue = menuHandler::FrameToggles;
screen->runNow();
} else if (selected == DisplayUnits) {
menuHandler::menuQueue = menuHandler::DisplayUnits;
screen->runNow();
} else { } else {
menuQueue = system_base_menu; menuQueue = system_base_menu;
screen->runNow(); screen->runNow();
@@ -1577,6 +1593,34 @@ void menuHandler::FrameToggles_menu()
screen->showOverlayBanner(bannerOptions); screen->showOverlayBanner(bannerOptions);
} }
void menuHandler::DisplayUnits_menu()
{
enum optionsNumbers { Back, MetricUnits, ImperialUnits };
static const char *optionsArray[] = {"Back", "Metric", "Imperial"};
BannerOverlayOptions bannerOptions;
bannerOptions.message = " Select display units";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = 3;
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
bannerOptions.InitialSelected = 2;
else
bannerOptions.InitialSelected = 1;
bannerOptions.bannerCallback = [](int selected) -> void {
if (selected == MetricUnits) {
config.display.units = meshtastic_Config_DisplayConfig_DisplayUnits_METRIC;
service->reloadConfig(SEGMENT_CONFIG);
} else if (selected == ImperialUnits) {
config.display.units = meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL;
service->reloadConfig(SEGMENT_CONFIG);
} else {
menuHandler::menuQueue = menuHandler::screen_options_menu;
screen->runNow();
}
};
screen->showOverlayBanner(bannerOptions);
}
void menuHandler::handleMenuSwitch(OLEDDisplay *display) void menuHandler::handleMenuSwitch(OLEDDisplay *display)
{ {
if (menuQueue != menu_none) if (menuQueue != menu_none)
@@ -1691,6 +1735,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case FrameToggles: case FrameToggles:
FrameToggles_menu(); FrameToggles_menu();
break; break;
case DisplayUnits:
DisplayUnits_menu();
break;
case throttle_message: case throttle_message:
screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000); screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
break; break;
@@ -1705,4 +1752,4 @@ void menuHandler::saveUIConfig()
} // namespace graphics } // namespace graphics
#endif #endif

View File

@@ -44,7 +44,8 @@ class menuHandler
trace_route_menu, trace_route_menu,
throttle_message, throttle_message,
node_name_length_menu, node_name_length_menu,
FrameToggles FrameToggles,
DisplayUnits
}; };
static screenMenus menuQueue; static screenMenus menuQueue;
@@ -88,6 +89,7 @@ class menuHandler
static void powerMenu(); static void powerMenu();
static void nodeNameLengthMenu(); static void nodeNameLengthMenu();
static void FrameToggles_menu(); static void FrameToggles_menu();
static void DisplayUnits_menu();
static void textMessageMenu(); static void textMessageMenu();
private: private:

View File

@@ -213,6 +213,7 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
#else #else
display->drawString(center_text, getTextPositions(display)[2], messageString); display->drawString(center_text, getTextPositions(display)[2], messageString);
#endif #endif
graphics::drawCommonFooter(display, x, y);
return; return;
} }
@@ -423,6 +424,7 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// Draw header at the end to sort out overlapping elements // Draw header at the end to sort out overlapping elements
graphics::drawCommonHeader(display, x, y, titleStr); graphics::drawCommonHeader(display, x, y, titleStr);
#endif #endif
graphics::drawCommonFooter(display, x, y);
} }
std::vector<std::string> generateLines(OLEDDisplay *display, const char *headerStr, const char *messageBuf, int textWidth) std::vector<std::string> generateLines(OLEDDisplay *display, const char *headerStr, const char *messageBuf, int textWidth)

View File

@@ -53,7 +53,7 @@ static int scrollIndex = 0;
// Utility Functions // Utility Functions
// ============================= // =============================
const char *getSafeNodeName(meshtastic_NodeInfoLite *node) const char *getSafeNodeName(OLEDDisplay *display, meshtastic_NodeInfoLite *node)
{ {
const char *name = NULL; const char *name = NULL;
static char nodeName[16] = "?"; static char nodeName[16] = "?";
@@ -81,6 +81,28 @@ const char *getSafeNodeName(meshtastic_NodeInfoLite *node)
snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF)); snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
} }
if (config.display.use_long_node_name == true) {
int availWidth = (SCREEN_WIDTH / 2) - 65;
if (availWidth < 0)
availWidth = 0;
size_t origLen = strlen(nodeName);
while (nodeName[0] && display->getStringWidth(nodeName) > availWidth) {
nodeName[strlen(nodeName) - 1] = '\0';
}
// If we actually truncated, append "..." (ensure space remains in buffer)
if (strlen(nodeName) < origLen) {
size_t len = strlen(nodeName);
size_t maxLen = sizeof(nodeName) - 4; // 3 for "..." and 1 for '\0'
if (len > maxLen) {
nodeName[maxLen] = '\0';
len = maxLen;
}
strcat(nodeName, "...");
}
}
return nodeName; return nodeName;
} }
@@ -147,7 +169,7 @@ void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
bool isLeftCol = (x < SCREEN_WIDTH / 2); bool isLeftCol = (x < SCREEN_WIDTH / 2);
int timeOffset = (isHighResolution) ? (isLeftCol ? 7 : 10) : (isLeftCol ? 3 : 7); int timeOffset = (isHighResolution) ? (isLeftCol ? 7 : 10) : (isLeftCol ? 3 : 7);
const char *nodeName = getSafeNodeName(node); const char *nodeName = getSafeNodeName(display, node);
char timeStr[10]; char timeStr[10];
uint32_t seconds = sinceLastSeen(node); uint32_t seconds = sinceLastSeen(node);
@@ -192,7 +214,7 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
int barsXOffset = columnWidth - barsOffset; int barsXOffset = columnWidth - barsOffset;
const char *nodeName = getSafeNodeName(node); const char *nodeName = getSafeNodeName(display, node);
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
@@ -236,7 +258,7 @@ void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
bool isLeftCol = (x < SCREEN_WIDTH / 2); bool isLeftCol = (x < SCREEN_WIDTH / 2);
int nameMaxWidth = columnWidth - (isHighResolution ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22)); int nameMaxWidth = columnWidth - (isHighResolution ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22));
const char *nodeName = getSafeNodeName(node); const char *nodeName = getSafeNodeName(display, node);
char distStr[10] = ""; char distStr[10] = "";
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
@@ -331,7 +353,7 @@ void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
// Adjust max text width depending on column and screen width // Adjust max text width depending on column and screen width
int nameMaxWidth = columnWidth - (isHighResolution ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22)); int nameMaxWidth = columnWidth - (isHighResolution ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22));
const char *nodeName = getSafeNodeName(node); const char *nodeName = getSafeNodeName(display, node);
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
@@ -362,11 +384,11 @@ void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
float bearing = GeoCoord::bearing(userLat, userLon, nodeLat, nodeLon); float bearing = GeoCoord::bearing(userLat, userLon, nodeLat, nodeLon);
float bearingToNode = RAD_TO_DEG * bearing; float bearingToNode = RAD_TO_DEG * bearing;
float relativeBearing = fmod((bearingToNode - myHeading + 360), 360); float relativeBearing = fmod((bearingToNode - myHeading + 360), 360);
float angle = relativeBearing * DEG_TO_RAD;
// Shrink size by 2px // Shrink size by 2px
int size = FONT_HEIGHT_SMALL - 5; int size = FONT_HEIGHT_SMALL - 5;
CompassRenderer::drawArrowToNode(display, centerX, centerY, size, relativeBearing); CompassRenderer::drawArrowToNode(display, centerX, centerY, size, relativeBearing);
/* /*
float angle = relativeBearing * DEG_TO_RAD;
float halfSize = size / 2.0; float halfSize = size / 2.0;
// Point of the arrow // Point of the arrow
@@ -403,6 +425,12 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
{ {
const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1; const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
const int rowYOffset = FONT_HEIGHT_SMALL - 3; const int rowYOffset = FONT_HEIGHT_SMALL - 3;
bool locationScreen = false;
if (strcmp(title, "Bearings") == 0)
locationScreen = true;
else if (strcmp(title, "Distance") == 0)
locationScreen = true;
#if defined(M5STACK_UNITC6L) #if defined(M5STACK_UNITC6L)
int columnWidth = display->getWidth(); int columnWidth = display->getWidth();
#else #else
@@ -418,7 +446,7 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
int totalEntries = nodeDB->getNumMeshNodes(); int totalEntries = nodeDB->getNumMeshNodes();
int totalRowsAvailable = (display->getHeight() - y) / rowYOffset; int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
int numskipped = 0;
int visibleNodeRows = totalRowsAvailable; int visibleNodeRows = totalRowsAvailable;
#if defined(M5STACK_UNITC6L) #if defined(M5STACK_UNITC6L)
int totalColumns = 1; int totalColumns = 1;
@@ -438,6 +466,10 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
int rowCount = 0; int rowCount = 0;
for (int i = startIndex; i < endIndex; ++i) { for (int i = startIndex; i < endIndex; ++i) {
if (locationScreen && !nodeDB->getMeshNodeByIndex(i)->has_position) {
numskipped++;
continue;
}
int xPos = x + (col * columnWidth); int xPos = x + (col * columnWidth);
int yPos = y + yOffset; int yPos = y + yOffset;
renderer(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth); renderer(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth);
@@ -460,6 +492,9 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
} }
} }
// This should correct the scrollbar
totalEntries -= numskipped;
#if !defined(M5STACK_UNITC6L) #if !defined(M5STACK_UNITC6L)
// Draw column separator // Draw column separator
if (shownCount > 0) { if (shownCount > 0) {
@@ -470,6 +505,7 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
#endif #endif
const int scrollStartY = y + 3; const int scrollStartY = y + 3;
drawScrollbar(display, visibleNodeRows, totalEntries, scrollIndex, 2, scrollStartY); drawScrollbar(display, visibleNodeRows, totalEntries, scrollIndex, 2, scrollStartY);
graphics::drawCommonFooter(display, x, y);
} }
// ============================= // =============================

View File

@@ -11,6 +11,7 @@
#include "graphics/Screen.h" #include "graphics/Screen.h"
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h" #include "graphics/SharedUIDisplay.h"
#include "graphics/TimeFormatters.h"
#include "graphics/images.h" #include "graphics/images.h"
#include "main.h" #include "main.h"
#include "target_specific.h" #include "target_specific.h"
@@ -256,7 +257,8 @@ void UIRenderer::drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const mes
} }
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ #if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \
defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS)) && \ defined(ST7789_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(HX8357_CS) || defined(ST7796_CS) || \
defined(HACKADAY_COMMUNICATOR) || defined(USE_ST7796)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS) !defined(DISPLAY_FORCE_SMALL_FONTS)
if (isHighResolution) { if (isHighResolution) {
@@ -383,17 +385,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
// === 4. Uptime (only show if metric is present) === // === 4. Uptime (only show if metric is present) ===
char uptimeStr[32] = ""; char uptimeStr[32] = "";
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) { if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
uint32_t uptime = node->device_metrics.uptime_seconds; getUptimeStr(node->device_metrics.uptime_seconds * 1000, " Up", uptimeStr, sizeof(uptimeStr));
uint32_t days = uptime / 86400;
uint32_t hours = (uptime % 86400) / 3600;
uint32_t mins = (uptime % 3600) / 60;
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
if (days)
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %ud %uh", days, hours);
else if (hours)
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %uh %um", hours, mins);
else
snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
} }
if (uptimeStr[0] && line < 5) { if (uptimeStr[0] && line < 5) {
display->drawString(x, getTextPositions(display)[line++], uptimeStr); display->drawString(x, getTextPositions(display)[line++], uptimeStr);
@@ -552,6 +544,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
// else show nothing // else show nothing
} }
#endif #endif
graphics::drawCommonFooter(display, x, y);
} }
// **************************** // ****************************
@@ -563,6 +556,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
int line = 1; int line = 1;
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
// === Header === // === Header ===
#if defined(M5STACK_UNITC6L) #if defined(M5STACK_UNITC6L)
@@ -590,18 +584,8 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online"); drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
#endif #endif
char uptimeStr[32] = ""; char uptimeStr[32] = "";
uint32_t uptime = millis() / 1000;
uint32_t days = uptime / 86400;
uint32_t hours = (uptime % 86400) / 3600;
uint32_t mins = (uptime % 3600) / 60;
// Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
#if !defined(M5STACK_UNITC6L) #if !defined(M5STACK_UNITC6L)
if (days) getUptimeStr(millis(), "Up", uptimeStr, sizeof(uptimeStr));
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %ud %uh", days, hours);
else if (hours)
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %uh %um", hours, mins);
else
snprintf(uptimeStr, sizeof(uptimeStr), "Up: %um", mins);
#endif #endif
display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr); display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr);
@@ -740,7 +724,6 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
int yOffset = (isHighResolution) ? 0 : 5; int yOffset = (isHighResolution) ? 0 : 5;
std::string longNameStr; std::string longNameStr;
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
if (ourNode && ourNode->has_user && strlen(ourNode->user.long_name) > 0) { if (ourNode && ourNode->has_user && strlen(ourNode->user.long_name) > 0) {
longNameStr = sanitizeString(ourNode->user.long_name); longNameStr = sanitizeString(ourNode->user.long_name);
} }
@@ -771,6 +754,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
display->drawString(nameX, getTextPositions(display)[line++], shortnameble); display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
} }
#endif #endif
graphics::drawCommonFooter(display, x, y);
} }
// Start Functions to write date/time to the screen // Start Functions to write date/time to the screen
@@ -1000,24 +984,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
const char *displayLine = ""; // Initialize to empty string by default const char *displayLine = ""; // Initialize to empty string by default
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
bool usePhoneGPS = (ourNode && nodeDB->hasValidPosition(ourNode) && if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED);
if (usePhoneGPS) {
// Phone-provided GPS is active
displayLine = "Phone GPS";
int yOffset = (isHighResolution) ? 3 : 1;
if (isHighResolution) {
NodeListRenderer::drawScaledXBitmap16x16(x, getTextPositions(display)[line] + yOffset - 5, imgSatellite_width,
imgSatellite_height, imgSatellite, display);
} else {
display->drawXbm(x + 1, getTextPositions(display)[line] + yOffset, imgSatellite_width, imgSatellite_height,
imgSatellite);
}
int xOffset = (isHighResolution) ? 6 : 0;
display->drawString(x + 11 + xOffset, getTextPositions(display)[line++], displayLine);
} else if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
// GPS disabled / not present
if (config.position.fixed_position) { if (config.position.fixed_position) {
displayLine = "Fixed GPS"; displayLine = "Fixed GPS";
} else { } else {
@@ -1063,36 +1030,17 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) { if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) {
// === Second Row: Last GPS Fix === // === Second Row: Last GPS Fix ===
if (gpsStatus->getLastFixMillis() > 0) { if (gpsStatus->getLastFixMillis() > 0) {
uint32_t delta = (millis() - gpsStatus->getLastFixMillis()) / 1000; // seconds since last fix uint32_t delta = millis() - gpsStatus->getLastFixMillis();
uint32_t days = delta / 86400; char uptimeStr[32];
uint32_t hours = (delta % 86400) / 3600;
uint32_t mins = (delta % 3600) / 60;
uint32_t secs = delta % 60;
char buf[32];
#if defined(USE_EINK) #if defined(USE_EINK)
// E-Ink: skip seconds, show only days/hours/mins // E-Ink: skip seconds, show only days/hours/mins
if (days > 0) { getUptimeStr(delta, "Last", uptimeStr, sizeof(uptimeStr), false);
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
} else if (hours > 0) {
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
} else {
snprintf(buf, sizeof(buf), "Last: %um", mins);
}
#else #else
// Non E-Ink: include seconds where useful // Non E-Ink: include seconds where useful
if (days > 0) { getUptimeStr(delta, "Last", uptimeStr, sizeof(uptimeStr), true);
snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
} else if (hours > 0) {
snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
} else if (mins > 0) {
snprintf(buf, sizeof(buf), "Last: %um %us", mins, secs);
} else {
snprintf(buf, sizeof(buf), "Last: %us", secs);
}
#endif #endif
display->drawString(0, getTextPositions(display)[line++], buf); display->drawString(0, getTextPositions(display)[line++], uptimeStr);
} else { } else {
display->drawString(0, getTextPositions(display)[line++], "Last: ?"); display->drawString(0, getTextPositions(display)[line++], "Last: ?");
} }
@@ -1108,9 +1056,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
// === Final Row: Altitude === // === Final Row: Altitude ===
char altitudeLine[32] = {0}; char altitudeLine[32] = {0};
int32_t alt = (strcmp(displayLine, "Phone GPS") == 0 && ourNode && nodeDB->hasValidPosition(ourNode)) int32_t alt = geoCoord.getAltitude();
? ourNode->position.altitude
: geoCoord.getAltitude();
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
snprintf(altitudeLine, sizeof(altitudeLine), "Alt: %.0fft", alt * METERS_TO_FEET); snprintf(altitudeLine, sizeof(altitudeLine), "Alt: %.0fft", alt * METERS_TO_FEET);
} else { } else {
@@ -1202,6 +1148,7 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
} }
#endif #endif
#endif // HAS_GPS #endif // HAS_GPS
graphics::drawCommonFooter(display, x, y);
} }
#ifdef USERPREFS_OEM_TEXT #ifdef USERPREFS_OEM_TEXT
@@ -1286,7 +1233,13 @@ void UIRenderer::drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *sta
if (totalIcons == 0) if (totalIcons == 0)
return; return;
const size_t iconsPerPage = (SCREEN_WIDTH + spacing) / (iconSize + spacing); const int navPadding = isHighResolution ? 24 : 12; // padding per side
int usableWidth = SCREEN_WIDTH - (navPadding * 2);
if (usableWidth < iconSize)
usableWidth = iconSize;
const size_t iconsPerPage = usableWidth / (iconSize + spacing);
const size_t currentPage = currentFrame / iconsPerPage; const size_t currentPage = currentFrame / iconsPerPage;
const size_t pageStart = currentPage * iconsPerPage; const size_t pageStart = currentPage * iconsPerPage;
const size_t pageEnd = min(pageStart + iconsPerPage, totalIcons); const size_t pageEnd = min(pageStart + iconsPerPage, totalIcons);
@@ -1357,6 +1310,47 @@ void UIRenderer::drawNavigationBar(OLEDDisplay *display, OLEDDisplayUiState *sta
display->setColor(WHITE); display->setColor(WHITE);
} }
} }
// Compact arrow drawer
auto drawArrow = [&](bool rightSide) {
display->setColor(WHITE);
const int offset = isHighResolution ? 3 : 1;
const int halfH = rectHeight / 2;
const int top = (y - 2) + (rectHeight - halfH) / 2;
const int bottom = top + halfH - 1;
const int midY = top + (halfH / 2);
const int maxW = 4;
// Determine left X coordinate
int baseX = rightSide ? (rectX + rectWidth + offset) : // right arrow
(rectX - offset - 1); // left arrow
for (int yy = top; yy <= bottom; yy++) {
int dist = abs(yy - midY);
int lineW = maxW - (dist * maxW / (halfH / 2));
if (lineW < 1)
lineW = 1;
if (rightSide) {
display->drawHorizontalLine(baseX, yy, lineW);
} else {
display->drawHorizontalLine(baseX - lineW + 1, yy, lineW);
}
}
};
// Right arrow
if (pageEnd < totalIcons) {
drawArrow(true);
}
// Left arrow
if (pageStart > 0) {
drawArrow(false);
}
// Knock the corners off the square // Knock the corners off the square
display->setColor(BLACK); display->setColor(BLACK);
display->drawRect(rectX, y - 2, 1, 1); display->drawRect(rectX, y - 2, 1, 1);
@@ -1391,4 +1385,4 @@ std::string UIRenderer::drawTimeDelta(uint32_t days, uint32_t hours, uint32_t mi
} // namespace graphics } // namespace graphics
#endif // HAS_SCREEN #endif // HAS_SCREEN

View File

@@ -1,3 +1,5 @@
#include "configuration.h"
#if HAS_SCREEN
#include "emotes.h" #include "emotes.h"
namespace graphics namespace graphics
@@ -16,6 +18,8 @@ const Emote emotes[] = {
{"\U0001F642", Slightly_Smiling, Slightly_Smiling_width, Slightly_Smiling_height}, // 🙂 Slightly Smiling Face {"\U0001F642", Slightly_Smiling, Slightly_Smiling_width, Slightly_Smiling_height}, // 🙂 Slightly Smiling Face
{"\U0001F609", Winking_Face, Winking_Face_width, Winking_Face_height}, // 😉 Winking Face {"\U0001F609", Winking_Face, Winking_Face_width, Winking_Face_height}, // 😉 Winking Face
{"\U0001F601", Grinning_Smiling_Eyes, Grinning_Smiling_Eyes_width, Grinning_Smiling_Eyes_height}, // 😁 Grinning Smiling Eyes {"\U0001F601", Grinning_Smiling_Eyes, Grinning_Smiling_Eyes_width, Grinning_Smiling_Eyes_height}, // 😁 Grinning Smiling Eyes
{"\U0001F60D", Heart_eyes, Heart_eyes_width, Heart_eyes_height}, // 😍 Heart Eyes
{"\U0001F970", heart_smile, heart_smile_width, heart_smile_height}, // 🥰 Smiling Face with Hearts
// --- Question/Alert --- // --- Question/Alert ---
{"\u2753", question, question_width, question_height}, // ❓ Question Mark {"\u2753", question, question_width, question_height}, // ❓ Question Mark
@@ -28,11 +32,15 @@ const Emote emotes[] = {
{"\U0001F605", haha, haha_width, haha_height}, // 😅 Smiling with Sweat {"\U0001F605", haha, haha_width, haha_height}, // 😅 Smiling with Sweat
{"\U0001F604", Grinning_SmilingEyes2, Grinning_SmilingEyes2_width, {"\U0001F604", Grinning_SmilingEyes2, Grinning_SmilingEyes2_width,
Grinning_SmilingEyes2_height}, // 😄 Grinning Face with Smiling Eyes Grinning_SmilingEyes2_height}, // 😄 Grinning Face with Smiling Eyes
{"\U0001F62D", Loudly_Crying_Face, Loudly_Crying_Face_width, Loudly_Crying_Face_height}, // 😭 Loudly Crying Face
// --- Gestures and People --- // --- Gestures and People ---
{"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height}, // 👋 Waving Hand {"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height}, // 👋 Waving Hand
{"\U0001F920", cowboy, cowboy_width, cowboy_height}, // 🤠 Cowboy Hat Face {"\u270C\uFE0F", peace_sign, peace_sign_width, peace_sign_height}, // ✌️ Victory Hand
{"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // 🎧 Headphones {"\U0001F596", vulcan_salute, vulcan_salute_width, vulcan_salute_height}, // 🖖 Vulcan Salute
{"\U0001F64F", Praying, Praying_width, Praying_height}, // 🙏 Praying Hands
{"\U0001F920", cowboy, cowboy_width, cowboy_height}, // 🤠 Cowboy Hat Face
{"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // 🎧 Headphones
// --- Weather --- // --- Weather ---
{"\u2600", sun, sun_width, sun_height}, // ☀ Sun (without variation selector) {"\u2600", sun, sun_width, sun_height}, // ☀ Sun (without variation selector)
@@ -43,8 +51,12 @@ const Emote emotes[] = {
// --- Misc Faces --- // --- Misc Faces ---
{"\U0001F608", devil, devil_width, devil_height}, // 😈 Smiling Face with Horns {"\U0001F608", devil, devil_width, devil_height}, // 😈 Smiling Face with Horns
{"\U0001F921", clown, clown_width, clown_height}, // 🤡 Clown Face
{"\U0001F916", robo, robo_width, robo_height}, // 🤖 Robot Face
// --- Hearts (Multiple Unicode Aliases) --- // --- Hearts (Multiple Unicode Aliases) ---
{"\u2665", heart, heart_width, heart_height}, // ♥ Black Heart Suit
{"\u2665\uFE0F", heart, heart_width, heart_height}, // ♥️ Black Heart Suit (emoji presentation)
{"\u2764\uFE0F", heart, heart_width, heart_height}, // ❤️ Red Heart {"\u2764\uFE0F", heart, heart_width, heart_height}, // ❤️ Red Heart
{"\U0001F9E1", heart, heart_width, heart_height}, // 🧡 Orange Heart {"\U0001F9E1", heart, heart_width, heart_height}, // 🧡 Orange Heart
{"\U00002763", heart, heart_width, heart_height}, // ❣ Heart Exclamation {"\U00002763", heart, heart_width, heart_height}, // ❣ Heart Exclamation
@@ -55,223 +67,167 @@ const Emote emotes[] = {
{"\U0001F498", heart, heart_width, heart_height}, // 💘 Heart with Arrow {"\U0001F498", heart, heart_width, heart_height}, // 💘 Heart with Arrow
// --- Objects --- // --- Objects ---
{"\U0001F4A9", poo, poo_width, poo_height}, // 💩 Pile of Poo {"\U0001F4A9", poo, poo_width, poo_height}, // 💩 Pile of Poo
{"\U0001F514", bell_icon, bell_icon_width, bell_icon_height} // 🔔 Bell {"\U0001F514", bell_icon, bell_icon_width, bell_icon_height}, // 🔔 Bell
{"\U0001F36A", cookie, cookie_width, cookie_height}, // 🍪 Cookie
{"\U0001F525", Fire, Fire_width, Fire_height}, // 🔥 Fire
{"\u2728", Sparkles, Sparkles_width, Sparkles_height}, // ✨ Sparkles
{"\U0001F573\uFE0F", hole, hole_width, hole_height}, // 🕳️ Hole
{"\U0001F3B3", bowling, bowling_width, bowling_height} // 🎳 Bowling
#endif #endif
}; };
const int numEmotes = sizeof(emotes) / sizeof(emotes[0]); const int numEmotes = sizeof(emotes) / sizeof(emotes[0]);
#ifndef EXCLUDE_EMOJI #ifndef EXCLUDE_EMOJI
const unsigned char thumbup[] PROGMEM = { const unsigned char thumbup[] PROGMEM = {0x00, 0x03, 0x80, 0x04, 0x80, 0x04, 0x40, 0x04, 0x20, 0x02, 0x18,
0x00, 0x1C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00, 0x02, 0x06, 0x3F, 0x06, 0x40, 0x06, 0x70, 0x06, 0x40, 0x06, 0x70,
0xC0, 0x08, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x06, 0x40, 0x06, 0x30, 0x08, 0x20, 0xF0, 0x1F, 0x00, 0x00};
0x0C, 0xCE, 0x7F, 0x00, 0x04, 0x20, 0x80, 0x00, 0x02, 0x20, 0x80, 0x00, 0x02, 0x60, 0xC0, 0x00, 0x01, 0xF8, 0xFF, 0x01,
0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0x18, 0x80, 0x00,
0x02, 0x30, 0xC0, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x38, 0x20, 0x10, 0x00, 0xE0, 0xCF, 0x1F, 0x00,
};
const unsigned char thumbdown[] PROGMEM = { const unsigned char thumbdown[] PROGMEM = {0xF0, 0x1F, 0x08, 0x20, 0x06, 0x30, 0x06, 0x40, 0x06, 0x70, 0x06,
0xE0, 0xCF, 0x1F, 0x00, 0x38, 0x20, 0x10, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x02, 0x30, 0xC0, 0x00, 0x40, 0x06, 0x70, 0x06, 0x40, 0x06, 0x3F, 0x18, 0x02, 0x20, 0x02,
0x01, 0x18, 0x80, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, 0x40, 0x04, 0x80, 0x04, 0x80, 0x04, 0x00, 0x03, 0x00, 0x00};
0x01, 0xF8, 0xFF, 0x01, 0x02, 0x60, 0xC0, 0x00, 0x02, 0x20, 0x80, 0x00, 0x04, 0x20, 0x80, 0x00, 0x0C, 0xCE, 0x7F, 0x00,
0x18, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00,
0x80, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
};
const unsigned char Smiling_Eyes[] PROGMEM = { const unsigned char Smiling_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1, 0x4A, 0x02, 0x40, 0x02, 0x40, 0x22, 0x44, 0x22, 0x44, 0xC2, 0x43,
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xff, 0xff, 0xcf, 0xfc, 0xff, 0xff, 0xcf, 0x04, 0x20, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0x7e, 0xf8, 0xc3, 0xdf, 0x3e, 0xf0, 0x81, 0xdf, 0xbf, 0xf7, 0xbd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0x3f, 0xff,
0x6f, 0xff, 0xdf, 0xfe, 0x6f, 0xff, 0xdf, 0xfe, 0x9f, 0xff, 0x3f, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x07, 0xc0};
const unsigned char Grinning[] PROGMEM = { const unsigned char Grinning[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1, 0x42, 0x02, 0x40, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf9, 0xf3, 0xcf, 0xfc, 0xf0, 0xe1, 0xcf, 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0xfe, 0xf0, 0xe1, 0xdf, 0xfe, 0xf0, 0xe1, 0xdf, 0xff, 0xf0, 0xe1, 0xff, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xbe, 0xff, 0xbf, 0xdf, 0x7e, 0x00, 0xc0, 0xdf,
0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char Slightly_Smiling[] PROGMEM = { const unsigned char Slightly_Smiling[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1, 0x42, 0x02, 0x40, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf9, 0xf3, 0xcf, 0xfc, 0xf0, 0xe1, 0xcf, 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0xfe, 0xf0, 0xe1, 0xdf, 0xfe, 0xf0, 0xe1, 0xdf, 0xff, 0xf0, 0xe1, 0xff, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char Winking_Face[] PROGMEM = { const unsigned char Winking_Face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x20, 0x42,
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1, 0x46, 0x02, 0x40, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
0xf0, 0xf0, 0xff, 0xc3, 0x78, 0xef, 0xc3, 0xc7, 0xb8, 0xdf, 0xbd, 0xcf, 0xfc, 0xf9, 0x7f, 0xcf, 0xfc, 0xf0, 0xff, 0xcf, 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0xfe, 0xf0, 0xc3, 0xdf, 0xfe, 0xf0, 0x81, 0xdf, 0xff, 0xf0, 0xbf, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x07, 0xc0};
const unsigned char Grinning_Smiling_Eyes[] PROGMEM = { const unsigned char Grinning_Smiling_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1, 0x4A, 0x02, 0x40, 0xFA, 0x5F, 0x0A, 0x50, 0x0A, 0x50, 0x12, 0x48,
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf8, 0xe3, 0xcf, 0x7c, 0xf7, 0xdd, 0xcf, 0x24, 0x24, 0xC4, 0x23, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0xbe, 0xef, 0xbe, 0xdf, 0xbe, 0xef, 0xbe, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0x5e, 0x55, 0x55, 0xdf, 0x5e, 0x55, 0x55, 0xdf,
0x3c, 0x00, 0x80, 0xcf, 0x7c, 0x55, 0xd5, 0xcf, 0xf8, 0x54, 0xe5, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char question[] PROGMEM = { const unsigned char heart_smile[] PROGMEM = {0x00, 0x00, 0x6C, 0x07, 0x7C, 0x18, 0x7C, 0x20, 0x38, 0x24, 0x52,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0xE0, 0xFF, 0x07, 0x00, 0x0A, 0x02, 0xD8, 0x02, 0xF8, 0x22, 0xFC, 0x20, 0x74, 0xDB, 0x23,
0xE0, 0xC3, 0x0F, 0x00, 0xF0, 0x81, 0x0F, 0x00, 0xF0, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x0F, 0x00, 0x1F, 0x00, 0x1F, 0x20, 0x0E, 0x18, 0xE4, 0x07, 0x00, 0x00};
0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x00,
0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const unsigned char bang[] PROGMEM = { const unsigned char Heart_eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x54, 0x2A, 0xFA,
0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x07, 0xF8, 0x3F, 0xFF, 0x07, 0xF8, 0x3F, 0x5F, 0x72, 0x4E, 0x22, 0x44, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48,
0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0x24, 0x24, 0xC4, 0x23, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0xFE, 0x03, 0xF0, 0x1F, 0xFE, 0x03, 0xF0, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F,
0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xC0, 0x03, 0xFC, 0x03, 0xF0, 0x0F, 0xFE, 0x03, 0xF0, 0x1F,
0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xF8, 0x01, 0xE0, 0x07,
};
const unsigned char haha[] PROGMEM = { const unsigned char question[] PROGMEM = {0xE0, 0x07, 0x10, 0x08, 0x08, 0x10, 0x88, 0x11, 0x48, 0x12, 0x48,
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0xe0, 0xf9, 0xf3, 0xc0, 0x12, 0x48, 0x12, 0x30, 0x11, 0x80, 0x08, 0x40, 0x04, 0x40, 0x02,
0xf0, 0xfe, 0xef, 0xc1, 0x38, 0xff, 0x9f, 0xc3, 0xd8, 0xff, 0x7f, 0xc3, 0xfc, 0xf8, 0xe3, 0xc7, 0x7c, 0xf7, 0xdd, 0xcf, 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x40, 0x02, 0x80, 0x01};
0xbe, 0xef, 0xbe, 0xcf, 0xfe, 0xff, 0xff, 0xcf, 0xef, 0xff, 0xff, 0xde, 0xe7, 0xff, 0xff, 0xdc, 0xeb, 0xff, 0xff, 0xda,
0xed, 0xff, 0xff, 0xd6, 0xee, 0xff, 0xff, 0xce, 0x36, 0x00, 0x80, 0xcd, 0xb8, 0xff, 0xbf, 0xc3, 0x7e, 0x00, 0xc0, 0xdf,
0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char ROFL[] PROGMEM = { const unsigned char bang[] PROGMEM = {0x30, 0x0C, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48,
0x00, 0x00, 0x00, 0xc0, 0x00, 0xfc, 0x07, 0xc0, 0x00, 0xff, 0x1f, 0xc0, 0x80, 0xff, 0x7f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x30, 0x0C,
0xe0, 0x9f, 0xff, 0xc1, 0xf0, 0x9f, 0xff, 0xc0, 0xf8, 0x9f, 0x7f, 0xcb, 0xf8, 0x9f, 0xbf, 0xcb, 0xfc, 0x9f, 0xdf, 0xdb, 0x00, 0x00, 0x30, 0x0C, 0x48, 0x12, 0x30, 0x0C, 0x00, 0x00};
0xfc, 0x1f, 0x08, 0xdc, 0xfe, 0x1f, 0xf8, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0x1e, 0xf0, 0x7f, 0xfe, 0x1e, 0xf0, 0xbf, 0xfe,
0xfe, 0xf3, 0xdf, 0xfe, 0xfe, 0xf3, 0x6f, 0xfe, 0xfe, 0xf3, 0x37, 0xfe, 0xfe, 0xeb, 0x1b, 0xfe, 0xfc, 0xef, 0x0d, 0xde,
0xfc, 0xe7, 0x06, 0xcf, 0xf8, 0x6b, 0x83, 0xcf, 0xf8, 0x0d, 0xc0, 0xc7, 0xf0, 0xed, 0xff, 0xc7, 0xe0, 0xee, 0xff, 0xc3,
0xc0, 0xee, 0xff, 0xc1, 0x80, 0xee, 0xff, 0xc0, 0x00, 0xe6, 0x3f, 0xc0, 0x00, 0xf0, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0xc0};
const unsigned char Smiling_Closed_Eyes[] PROGMEM = { const unsigned char haha[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1, 0x4A, 0x0A, 0x50, 0x0E, 0x70, 0xF2, 0x4F, 0x12, 0x48, 0x32, 0x44,
0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0x7c, 0xfe, 0xcf, 0xcf, 0xfc, 0xfc, 0xe7, 0xcf, 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0xfe, 0xf9, 0xf3, 0xdf, 0xfe, 0xf3, 0xf9, 0xdf, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xfc, 0xe7, 0xff, 0x7f, 0xfe, 0xcf, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xbe, 0xff, 0xbf, 0xdf, 0x7e, 0x00, 0xc0, 0xdf,
0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char Grinning_SmilingEyes2[] PROGMEM = { const unsigned char ROFL[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x84, 0x21, 0x84, 0x20, 0x02,
0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0xe0, 0xff, 0xff, 0xc0, 0x4C, 0x02, 0x4A, 0x1A, 0x49, 0x8A, 0x48, 0x42, 0x48, 0x22, 0x44,
0xf0, 0xff, 0xff, 0xc1, 0xf8, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc3, 0xfc, 0xf8, 0xe3, 0xc7, 0x7c, 0xf7, 0xdd, 0xc7, 0xE4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0xbe, 0xef, 0xbe, 0xcf, 0xfe, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf,
0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf, 0x3f, 0x00, 0x80, 0xdf, 0xbe, 0xff, 0xbf, 0xcf, 0x7e, 0x00, 0xc0, 0xcf,
0x7c, 0x00, 0xc0, 0xc7, 0xfc, 0x00, 0xe0, 0xc7, 0xf8, 0x01, 0xf0, 0xc3, 0xf8, 0x03, 0xf8, 0xc3, 0xf0, 0xff, 0xff, 0xc1,
0xe0, 0xff, 0xff, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
const unsigned char wave_icon[] PROGMEM = { const unsigned char Smiling_Closed_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x42,
0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0xc0, 0xc1, 0x00, 0x00, 0x00, 0xc7, 0x42, 0x22, 0x44, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
0x00, 0x00, 0x1e, 0xcc, 0x00, 0x00, 0x30, 0xc8, 0x00, 0x00, 0x60, 0xd8, 0x00, 0x08, 0xc0, 0xd0, 0x00, 0x1a, 0x81, 0xd1, 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0x00, 0x36, 0x03, 0xd3, 0x80, 0x6d, 0x06, 0xd2, 0x00, 0xdb, 0x0c, 0xc2, 0x80, 0xb6, 0x1d, 0xc0, 0x80, 0x6d, 0x1f, 0xc0,
0x00, 0xdb, 0x3f, 0xc0, 0x00, 0xf6, 0x7f, 0xc0, 0x00, 0xfc, 0x7f, 0xc0, 0x08, 0xf8, 0x7f, 0xc0, 0x48, 0xf0, 0x7f, 0xc0,
0x48, 0xe0, 0x7f, 0xc0, 0xc8, 0xc0, 0x3f, 0xc0, 0x98, 0x81, 0x1f, 0xc0, 0x10, 0x03, 0x00, 0xc0, 0x30, 0x0e, 0x00, 0xc0,
0x20, 0x38, 0x00, 0xc0, 0xe0, 0x00, 0x00, 0xc0, 0x80, 0x07, 0x00, 0xc0, 0x00, 0x1e, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0};
const unsigned char cowboy[] PROGMEM = { const unsigned char Grinning_SmilingEyes2[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
0x00, 0x0c, 0x0c, 0xc0, 0x00, 0x02, 0x10, 0xc0, 0x00, 0x01, 0x20, 0xc0, 0xbc, 0x00, 0x40, 0xcf, 0xc2, 0x01, 0xe0, 0xd0, 0x4A, 0x02, 0x40, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
0x01, 0x01, 0x20, 0xe0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0xc1, 0x3f, 0xff, 0xe0, 0xe1, 0xff, 0xff, 0xe1, 0xf2, 0xf3, 0xf3, 0xd3, 0xf4, 0xf1, 0xe3, 0xcb, 0xfc, 0xf1, 0xe3, 0xc7,
0xf8, 0xf1, 0xe3, 0xc7, 0xf8, 0xf1, 0xe3, 0xc7, 0xf8, 0xfb, 0xf7, 0xc7, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xc7,
0x70, 0xf8, 0x8f, 0xc3, 0x70, 0x03, 0xb0, 0xc3, 0x70, 0xfe, 0xbf, 0xc3, 0x60, 0x00, 0x80, 0xc1, 0xc0, 0x00, 0xc0, 0xc0,
0x80, 0x01, 0x60, 0xc0, 0x00, 0x07, 0x38, 0xc0, 0x00, 0xfe, 0x1f, 0xc0, 0x00, 0xf0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0xc0};
const unsigned char deadmau5[] PROGMEM = { const unsigned char Loudly_Crying_Face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x34, 0x2C, 0x4A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x52, 0x12, 0x48, 0x12, 0x48, 0x92, 0x49, 0x52, 0x4A, 0x52, 0x4A,
0x00, 0xFC, 0x03, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0x00, 0x54, 0x2A, 0x94, 0x29, 0x18, 0x18, 0xF0, 0x0F, 0x00, 0x00};
0xE0, 0xFF, 0xFF, 0x01, 0xF0, 0xFF, 0x7F, 0x00, 0xF0, 0xFF, 0xFF, 0x03, 0xF8, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x07,
0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFE, 0xFF, 0xFF, 0x00,
0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0xFC,
0x0F, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0xF8, 0x0F, 0xFC, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0xF8, 0x1F, 0xFC, 0x1F, 0x00,
0x00, 0xFF, 0x0F, 0xFC, 0x3F, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x1F, 0xFF, 0xFF, 0xFE, 0x01, 0x00, 0x00, 0x00, 0xFC, 0xFF,
0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const unsigned char sun[] PROGMEM = { const unsigned char wave_icon[] PROGMEM = {0x00, 0x00, 0xC0, 0x18, 0x30, 0x21, 0x48, 0x5A, 0x94, 0x64, 0x24,
0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x30, 0xC0, 0x00, 0x03, 0x25, 0x4A, 0x24, 0x12, 0x44, 0x22, 0x44, 0x04, 0x40, 0x08, 0x40,
0x70, 0x00, 0x80, 0x03, 0xF0, 0x00, 0xC0, 0x03, 0xF0, 0xF8, 0xC7, 0x03, 0xE0, 0xFC, 0xCF, 0x01, 0x00, 0xFE, 0x1F, 0x00, 0x12, 0x40, 0x22, 0x20, 0xC4, 0x10, 0x18, 0x0F, 0x00, 0x00};
0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x8E, 0xFF, 0x7F, 0x1C, 0x9F, 0xFF, 0x7F, 0x3E,
0x9F, 0xFF, 0x7F, 0x3E, 0x8E, 0xFF, 0x7F, 0x1C, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00,
0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0xC0, 0xF9, 0xE7, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0xE0, 0x03,
0xF0, 0xC0, 0xC0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00,
};
const unsigned char rain[] PROGMEM = { const unsigned char cowboy[] PROGMEM = {0x70, 0x0E, 0x8F, 0xF1, 0x11, 0x88, 0x21, 0x84, 0xC2, 0x43, 0x1E,
0xC0, 0x0F, 0xC0, 0x00, 0x40, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x03, 0x38, 0x00, 0x78, 0xE2, 0x47, 0x42, 0x42, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
0x00, 0x0E, 0x0C, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x20, 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x30, 0x02, 0x00,
0x00, 0x10, 0x06, 0x00, 0x00, 0x08, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x01, 0x80, 0x00, 0x01, 0x00,
0xC0, 0xC0, 0x81, 0x03, 0xA0, 0x60, 0xC1, 0x03, 0x90, 0x20, 0x41, 0x01, 0xF0, 0xE0, 0xC0, 0x01, 0x60, 0x4C,
0x98, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0x0B, 0x12, 0x00, 0x00, 0x09, 0x1A, 0x00, 0x00, 0x06, 0x0E, 0x00,
};
const unsigned char cloud[] PROGMEM = { const unsigned char deadmau5[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0xE4, 0x27, 0x12, 0x48, 0x0A,
0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x70, 0x30, 0x00, 0x00, 0x10, 0x60, 0x00, 0x80, 0x1F, 0x40, 0x00, 0x50, 0x0E, 0x70, 0x11, 0x88, 0x19, 0x98, 0x19, 0x98, 0x19, 0x98,
0xC0, 0x0F, 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x01, 0x19, 0x98, 0x19, 0x98, 0x11, 0x88, 0x0E, 0x70, 0x00, 0x00};
0x20, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10,
0x02, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x10,
0x02, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03,
};
const unsigned char fog[] PROGMEM = { const unsigned char sun[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0xEC, 0x37, 0xFC, 0x3F, 0xF8, 0x1F, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFE, 0x7F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFE, 0x7F, 0xFC, 0x3F,
0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x3C, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01, 0xF8, 0x1F, 0xFC, 0x3F, 0xEC, 0x37, 0x80, 0x01, 0x00, 0x00};
0x00, 0x38, 0x00, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0xFF, 0x83, 0xFF, 0x01, 0x03, 0xFF, 0x81, 0x01, 0x00, 0x7C, 0x00, 0x00,
0xF8, 0x00, 0x3E, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01, 0x00, 0x38, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const unsigned char devil[] PROGMEM = { const unsigned char rain[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x38, 0x1F, 0xFC, 0x3F, 0xFE,
0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x0f, 0xfc, 0x0f, 0xfc, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0x00, 0x00, 0x48, 0x12,
0x3f, 0xff, 0x3f, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0xfe, 0xff, 0xff, 0xdf, 0xfe, 0xff, 0xff, 0xdf, 0xfc, 0xff, 0xff, 0xcf, 0x48, 0x12, 0x24, 0x09, 0x24, 0x09, 0x00, 0x00, 0x00, 0x00};
0xfc, 0xff, 0xff, 0xcf, 0xf8, 0xff, 0xff, 0xc7, 0xf0, 0xff, 0xff, 0xc3, 0xf0, 0xff, 0xff, 0xc3, 0xf0, 0xf1, 0xe3, 0xc3,
0xf0, 0xe7, 0xf9, 0xc3, 0xf0, 0xe7, 0xf9, 0xc3, 0xf0, 0xe3, 0xf1, 0xc3, 0xf0, 0xe3, 0xf1, 0xc3, 0xf0, 0xe7, 0xf9, 0xc3,
0xf0, 0xff, 0xff, 0xc3, 0xe0, 0xfd, 0xef, 0xc1, 0xe0, 0xf3, 0xf3, 0xc1, 0xc0, 0x07, 0xf8, 0xc0, 0x80, 0x1f, 0x7e, 0xc0,
0x00, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0};
const unsigned char heart[] PROGMEM = { const unsigned char cloud[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x38, 0x1F, 0xFC,
0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xF0, 0x00, 0xF8, 0x0F, 0xFC, 0x07, 0xFC, 0x1F, 0x06, 0x0E, 0xFE, 0x3F, 0x03, 0x18, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0xF8, 0x1F,
0xFE, 0xFF, 0x7F, 0x10, 0xFF, 0xFF, 0xFF, 0x31, 0xFF, 0xFF, 0xFF, 0x33, 0xFF, 0xFF, 0xFF, 0x37, 0xFF, 0xFF, 0xFF, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0x1F,
0xFC, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0x03,
0xE0, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0xFE, 0x1F, 0x00,
0x00, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
};
const unsigned char poo[] PROGMEM = { const unsigned char fog[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x54, 0x55, 0x22, 0x22, 0x00,
0x00, 0x1c, 0x00, 0xc0, 0x00, 0x7c, 0x00, 0xc0, 0x00, 0xfc, 0x00, 0xc0, 0x00, 0x7c, 0x03, 0xc0, 0x00, 0xbe, 0x03, 0xc0, 0x00, 0x44, 0x44, 0xAA, 0x2A, 0x11, 0x11, 0x00, 0x00, 0x88, 0x88,
0x00, 0xdf, 0x0f, 0xc0, 0x80, 0xcf, 0x0f, 0xc0, 0xc0, 0xf1, 0x0f, 0xc0, 0x60, 0xfc, 0x0f, 0xc0, 0x30, 0xff, 0x07, 0xc0, 0x54, 0x55, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
0x90, 0xff, 0x3b, 0xc0, 0xc0, 0xff, 0x7d, 0xc0, 0xf8, 0xff, 0xfc, 0xc0, 0xf8, 0x3f, 0xf0, 0xc0, 0x78, 0x88, 0xc0, 0xc0,
0x20, 0xe3, 0x18, 0xc0, 0x98, 0xe7, 0xbc, 0xc1, 0x9c, 0x64, 0xa4, 0xc3, 0x9e, 0x64, 0xa4, 0xc7, 0xbe, 0xe4, 0xa4, 0xc7,
0xbc, 0x27, 0xbc, 0xc7, 0x38, 0x03, 0xd9, 0xc3, 0x00, 0xf0, 0x63, 0xc0, 0xf8, 0xfc, 0x3f, 0xcf, 0xfc, 0xff, 0x87, 0xdf,
0xfe, 0xff, 0xe0, 0xdf, 0xfc, 0x1f, 0xfe, 0xdf, 0xf8, 0x07, 0xf8, 0xcf, 0xf0, 0x03, 0xe0, 0xc7, 0x00, 0x00, 0x00, 0xc0};
const unsigned char bell_icon[] PROGMEM = { const unsigned char devil[] PROGMEM = {0x06, 0x60, 0xCA, 0x53, 0x32, 0x4C, 0x22, 0x44, 0x44, 0x22, 0x3A,
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11110000, 0x5C, 0x32, 0x4C, 0x52, 0x4A, 0x72, 0x4E, 0x02, 0x40, 0x22, 0x44,
0b00000011, 0b00000000, 0b00000000, 0b11111100, 0b00001111, 0b00000000, 0b00000000, 0b00001111, 0b00111100, 0b00000000, 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
0b00000000, 0b00000011, 0b00110000, 0b00000000, 0b10000000, 0b00000001, 0b01100000, 0b00000000, 0b11000000, 0b00000000,
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, const unsigned char heart[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x7E, 0x7E, 0xFE, 0x7F, 0xFE,
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0xF8, 0x1F, 0xF8, 0x1F,
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0xF0, 0x0F, 0xE0, 0x07, 0xC0, 0x03, 0x80, 0x01, 0x00, 0x00};
0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b01000000, 0b00000000, 0b10000000, 0b00000000, 0b01100000, 0b00000000,
0b10000000, 0b00000001, 0b01110000, 0b00000000, 0b10000000, 0b00000011, 0b00110000, 0b00000000, 0b00000000, 0b00000011, const unsigned char poo[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x20, 0x04, 0x10, 0x04, 0xF0,
0b00011000, 0b00000000, 0b00000000, 0b00000110, 0b11110000, 0b11111111, 0b11111111, 0b00000011, 0b00000000, 0b00001100, 0x08, 0x10, 0x10, 0x48, 0x12, 0x08, 0x18, 0xE8, 0x21, 0x1C, 0x40,
0b00001100, 0b00000000, 0b00000000, 0b00011000, 0b00000110, 0b00000000, 0b00000000, 0b11111000, 0b00000111, 0b00000000, 0x42, 0x42, 0x82, 0x41, 0x02, 0x30, 0xFC, 0x0F, 0x00, 0x00};
0b00000000, 0b11100000, 0b00000001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000}; const unsigned char bell_icon[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0xE0, 0x07, 0xF0, 0x0F, 0xF0,
0x0F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFC, 0x3F,
0xFC, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0x80, 0x01, 0x00, 0x00};
const unsigned char cookie[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x34, 0x22, 0x32,
0x40, 0x02, 0x58, 0x82, 0x5B, 0x92, 0x43, 0x82, 0x43, 0x02, 0x40,
0x64, 0x28, 0x64, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char Fire[] PROGMEM = {0x30, 0x00, 0xF0, 0x00, 0xF8, 0x03, 0xF8, 0x07, 0xFC, 0x1F, 0xFC,
0x1F, 0xFE, 0x3E, 0x7E, 0x3E, 0x3E, 0x7C, 0x1E, 0x78, 0x1E, 0x70,
0x1C, 0x70, 0x1C, 0x70, 0x38, 0x38, 0x30, 0x38, 0x60, 0x0C};
const unsigned char peace_sign[] PROGMEM = {0xC0, 0x30, 0x40, 0x29, 0x40, 0x25, 0x40, 0x15, 0x40, 0x12, 0x38,
0x0A, 0x54, 0x68, 0x54, 0x58, 0x54, 0x44, 0x3C, 0x22, 0x04, 0x22,
0x04, 0x12, 0x08, 0x10, 0x10, 0x08, 0xE0, 0x07, 0x00, 0x00};
const unsigned char Praying[] PROGMEM = {0x00, 0x00, 0x40, 0x02, 0xA0, 0x05, 0x90, 0x09, 0x90, 0x09, 0x90,
0x09, 0x98, 0x19, 0x94, 0x29, 0xA4, 0x25, 0xA4, 0x25, 0x84, 0x21,
0x84, 0x21, 0x86, 0x61, 0x4E, 0x72, 0x7F, 0x7E, 0x3F, 0xFC};
const unsigned char Sparkles[] PROGMEM = {0x00, 0x00, 0x10, 0x00, 0x38, 0x04, 0x10, 0x04, 0x00, 0x0E, 0x00,
0x1F, 0x80, 0x3F, 0xE0, 0xFF, 0x80, 0x3F, 0x10, 0x1F, 0x10, 0x0E,
0x38, 0x04, 0xFE, 0x04, 0x38, 0x00, 0x10, 0x00, 0x10, 0x00};
const unsigned char clown[] PROGMEM = {0x00, 0x00, 0xEE, 0x77, 0x1A, 0x58, 0x06, 0x60, 0x24, 0x24, 0x72,
0x4E, 0x22, 0x44, 0x82, 0x41, 0x82, 0x41, 0x1A, 0x58, 0xF2, 0x4F,
0x14, 0x28, 0xE4, 0x27, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
const unsigned char robo[] PROGMEM = {0x80, 0x01, 0xC0, 0x03, 0x80, 0x01, 0xFC, 0x3F, 0x04, 0x20, 0x74,
0x2E, 0x52, 0x4A, 0x72, 0x4E, 0x02, 0x40, 0x02, 0x40, 0xA2, 0x4A,
0x52, 0x45, 0x04, 0x20, 0x04, 0x20, 0xFC, 0x3F, 0x00, 0x00};
const unsigned char hole[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x3C, 0x3C,
0x06, 0x60, 0x0C, 0x30, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00};
const unsigned char bowling[] PROGMEM = {0x00, 0x38, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00,
0x38, 0x00, 0x28, 0x78, 0x44, 0x84, 0x82, 0x22, 0x83, 0x52, 0x83,
0x02, 0x83, 0x02, 0x45, 0x84, 0x44, 0x78, 0x38, 0x00, 0x00};
const unsigned char vulcan_salute[] PROGMEM = {0x08, 0x02, 0x16, 0x0D, 0x15, 0x15, 0x15, 0x15, 0xA9, 0x12, 0x4A,
0x0A, 0x02, 0x38, 0x04, 0x48, 0x04, 0x44, 0x04, 0x22, 0x04, 0x22,
0x04, 0x12, 0x08, 0x10, 0x10, 0x08, 0xE0, 0x07, 0x00, 0x00};
#endif #endif
} // namespace graphics } // namespace graphics
#endif

View File

@@ -17,98 +17,150 @@ extern const int numEmotes;
#ifndef EXCLUDE_EMOJI #ifndef EXCLUDE_EMOJI
// === Emote Bitmaps === // === Emote Bitmaps ===
#define thumbs_height 25 #define thumbs_height 16
#define thumbs_width 25 #define thumbs_width 16
extern const unsigned char thumbup[] PROGMEM; extern const unsigned char thumbup[] PROGMEM;
extern const unsigned char thumbdown[] PROGMEM; extern const unsigned char thumbdown[] PROGMEM;
#define Smiling_Eyes_height 30 #define Smiling_Eyes_height 16
#define Smiling_Eyes_width 30 #define Smiling_Eyes_width 16
extern const unsigned char Smiling_Eyes[] PROGMEM; extern const unsigned char Smiling_Eyes[] PROGMEM;
#define Grinning_height 30 #define Grinning_height 16
#define Grinning_width 30 #define Grinning_width 16
extern const unsigned char Grinning[] PROGMEM; extern const unsigned char Grinning[] PROGMEM;
#define Slightly_Smiling_height 30 #define Slightly_Smiling_height 16
#define Slightly_Smiling_width 30 #define Slightly_Smiling_width 16
extern const unsigned char Slightly_Smiling[] PROGMEM; extern const unsigned char Slightly_Smiling[] PROGMEM;
#define Winking_Face_height 30 #define Winking_Face_height 16
#define Winking_Face_width 30 #define Winking_Face_width 16
extern const unsigned char Winking_Face[] PROGMEM; extern const unsigned char Winking_Face[] PROGMEM;
#define Grinning_Smiling_Eyes_height 30 #define Grinning_Smiling_Eyes_height 16
#define Grinning_Smiling_Eyes_width 30 #define Grinning_Smiling_Eyes_width 16
extern const unsigned char Grinning_Smiling_Eyes[] PROGMEM; extern const unsigned char Grinning_Smiling_Eyes[] PROGMEM;
#define question_height 25 #define heart_smile_height 16
#define question_width 25 #define heart_smile_width 16
extern const unsigned char heart_smile[] PROGMEM;
#define Heart_eyes_height 16
#define Heart_eyes_width 16
extern const unsigned char Heart_eyes[] PROGMEM;
#define question_height 16
#define question_width 16
extern const unsigned char question[] PROGMEM; extern const unsigned char question[] PROGMEM;
#define bang_height 30 #define bang_height 16
#define bang_width 30 #define bang_width 16
extern const unsigned char bang[] PROGMEM; extern const unsigned char bang[] PROGMEM;
#define haha_height 30 #define haha_height 16
#define haha_width 30 #define haha_width 16
extern const unsigned char haha[] PROGMEM; extern const unsigned char haha[] PROGMEM;
#define ROFL_height 30 #define ROFL_height 16
#define ROFL_width 30 #define ROFL_width 16
extern const unsigned char ROFL[] PROGMEM; extern const unsigned char ROFL[] PROGMEM;
#define Smiling_Closed_Eyes_height 30 #define Smiling_Closed_Eyes_height 16
#define Smiling_Closed_Eyes_width 30 #define Smiling_Closed_Eyes_width 16
extern const unsigned char Smiling_Closed_Eyes[] PROGMEM; extern const unsigned char Smiling_Closed_Eyes[] PROGMEM;
#define Grinning_SmilingEyes2_height 30 #define Grinning_SmilingEyes2_height 16
#define Grinning_SmilingEyes2_width 30 #define Grinning_SmilingEyes2_width 16
extern const unsigned char Grinning_SmilingEyes2[] PROGMEM; extern const unsigned char Grinning_SmilingEyes2[] PROGMEM;
#define wave_icon_height 30 #define Loudly_Crying_Face_height 16
#define wave_icon_width 30 #define Loudly_Crying_Face_width 16
extern const unsigned char Loudly_Crying_Face[] PROGMEM;
#define wave_icon_height 16
#define wave_icon_width 16
extern const unsigned char wave_icon[] PROGMEM; extern const unsigned char wave_icon[] PROGMEM;
#define cowboy_height 30 #define cowboy_height 16
#define cowboy_width 30 #define cowboy_width 16
extern const unsigned char cowboy[] PROGMEM; extern const unsigned char cowboy[] PROGMEM;
#define deadmau5_height 30 #define deadmau5_height 16
#define deadmau5_width 60 #define deadmau5_width 16
extern const unsigned char deadmau5[] PROGMEM; extern const unsigned char deadmau5[] PROGMEM;
#define sun_height 30 #define sun_height 16
#define sun_width 30 #define sun_width 16
extern const unsigned char sun[] PROGMEM; extern const unsigned char sun[] PROGMEM;
#define rain_height 30 #define rain_height 16
#define rain_width 30 #define rain_width 16
extern const unsigned char rain[] PROGMEM; extern const unsigned char rain[] PROGMEM;
#define cloud_height 30 #define cloud_height 16
#define cloud_width 30 #define cloud_width 16
extern const unsigned char cloud[] PROGMEM; extern const unsigned char cloud[] PROGMEM;
#define fog_height 25 #define fog_height 16
#define fog_width 25 #define fog_width 16
extern const unsigned char fog[] PROGMEM; extern const unsigned char fog[] PROGMEM;
#define devil_height 30 #define devil_height 16
#define devil_width 30 #define devil_width 16
extern const unsigned char devil[] PROGMEM; extern const unsigned char devil[] PROGMEM;
#define heart_height 30 #define heart_height 16
#define heart_width 30 #define heart_width 16
extern const unsigned char heart[] PROGMEM; extern const unsigned char heart[] PROGMEM;
#define poo_height 30 #define poo_height 16
#define poo_width 30 #define poo_width 16
extern const unsigned char poo[] PROGMEM; extern const unsigned char poo[] PROGMEM;
#define bell_icon_width 30 #define bell_icon_width 16
#define bell_icon_height 30 #define bell_icon_height 16
extern const unsigned char bell_icon[] PROGMEM; extern const unsigned char bell_icon[] PROGMEM;
#define cookie_width 16
#define cookie_height 16
extern const unsigned char cookie[] PROGMEM;
#define Fire_width 16
#define Fire_height 16
extern const unsigned char Fire[] PROGMEM;
#define peace_sign_width 16
#define peace_sign_height 16
extern const unsigned char peace_sign[] PROGMEM;
#define Praying_width 16
#define Praying_height 16
extern const unsigned char Praying[] PROGMEM;
#define Sparkles_width 16
#define Sparkles_height 16
extern const unsigned char Sparkles[] PROGMEM;
#define clown_width 16
#define clown_height 16
extern const unsigned char clown[] PROGMEM;
#define robo_width 16
#define robo_height 16
extern const unsigned char robo[] PROGMEM;
#define hole_width 16
#define hole_height 16
extern const unsigned char hole[] PROGMEM;
#define bowling_width 16
#define bowling_height 16
extern const unsigned char bowling[] PROGMEM;
#define vulcan_salute_width 16
#define vulcan_salute_height 16
extern const unsigned char vulcan_salute[] PROGMEM;
#endif // EXCLUDE_EMOJI #endif // EXCLUDE_EMOJI
} // namespace graphics } // namespace graphics

View File

@@ -28,7 +28,7 @@ const uint8_t bluetoothConnectedIcon[36] PROGMEM = {0xfe, 0x01, 0xff, 0x03, 0x03
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || defined(ST7735_CS) || \ #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) || \
ARCH_PORTDUINO) && \ defined(USE_ST7796) || defined(HACKADAY_COMMUNICATOR) || ARCH_PORTDUINO) && \
!defined(DISPLAY_FORCE_SMALL_FONTS) !defined(DISPLAY_FORCE_SMALL_FONTS)
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff}; const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f}; const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
@@ -360,6 +360,10 @@ const uint8_t chirpy_hirez[] = {
#define chirpy_small_image_height 8 #define chirpy_small_image_height 8
const uint8_t chirpy_small[] = {0x7f, 0x41, 0x55, 0x55, 0x55, 0x55, 0x41, 0x7f}; const uint8_t chirpy_small[] = {0x7f, 0x41, 0x55, 0x55, 0x55, 0x55, 0x41, 0x7f};
#define connection_icon_width 7
#define connection_icon_height 5
const uint8_t connection_icon[] = {0x36, 0x41, 0x5D, 0x41, 0x36};
#ifdef M5STACK_UNITC6L #ifdef M5STACK_UNITC6L
#include "img/icon_small.xbm" #include "img/icon_small.xbm"
#else #else

View File

@@ -13,45 +13,147 @@ void InkHUD::MapApplet::onRender()
return; return;
} }
// Helper: draw rounded rectangle centered at x,y
auto fillRoundedRect = [&](int16_t cx, int16_t cy, int16_t w, int16_t h, int16_t r, uint16_t color) {
int16_t x = cx - (w / 2);
int16_t y = cy - (h / 2);
// center rects
fillRect(x + r, y, w - 2 * r, h, color);
fillRect(x, y + r, r, h - 2 * r, color);
fillRect(x + w - r, y + r, r, h - 2 * r, color);
// corners
fillCircle(x + r, y + r, r, color);
fillCircle(x + w - r - 1, y + r, r, color);
fillCircle(x + r, y + h - r - 1, r, color);
fillCircle(x + w - r - 1, y + h - r - 1, r, color);
};
// Find center of map // Find center of map
// - latitude and longitude
// - will be placed at X(0.5), Y(0.5)
getMapCenter(&latCenter, &lngCenter); getMapCenter(&latCenter, &lngCenter);
// Calculate North+East distance of each node to map center
// - which nodes to use controlled by virtual shouldDrawNode method
calculateAllMarkers(); calculateAllMarkers();
// Set the region shown on the map
// - default: fit all nodes, plus padding
// - maybe overriden by derived applet
// - getMapSize *sets* passed parameters (C-style)
getMapSize(&widthMeters, &heightMeters); getMapSize(&widthMeters, &heightMeters);
// Set the metersToPx conversion value
calculateMapScale(); calculateMapScale();
// Special marker for own node // Draw all markers first
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
if (ourNode && nodeDB->hasValidPosition(ourNode))
drawLabeledMarker(ourNode);
// Draw all markers
for (Marker m : markers) { for (Marker m : markers) {
int16_t x = X(0.5) + (m.eastMeters * metersToPx); int16_t x = X(0.5) + (m.eastMeters * metersToPx);
int16_t y = Y(0.5) - (m.northMeters * metersToPx); int16_t y = Y(0.5) - (m.northMeters * metersToPx);
// Cross Size // Add white halo outline first
constexpr uint16_t csMin = 5; constexpr int outlinePad = 1;
constexpr uint16_t csMax = 12; int boxSize = 11;
int radius = 2; // rounded corner radius
// Too many hops away // White halo background
if (m.hasHopsAway && m.hopsAway > config.lora.hop_limit) // Too many mops fillRoundedRect(x, y, boxSize + (outlinePad * 2), boxSize + (outlinePad * 2), radius + 1, WHITE);
printAt(x, y, "!", CENTER, MIDDLE);
else if (!m.hasHopsAway) // Unknown hops // Draw inner box
drawCross(x, y, csMin); fillRoundedRect(x, y, boxSize, boxSize, radius, BLACK);
else // The fewer hops, the larger the cross
drawCross(x, y, map(m.hopsAway, 0, config.lora.hop_limit, csMax, csMin)); // Text inside
setFont(fontSmall);
setTextColor(WHITE);
// Draw actual marker on top
if (m.hasHopsAway && m.hopsAway > config.lora.hop_limit) {
printAt(x + 1, y + 1, "X", CENTER, MIDDLE);
} else if (!m.hasHopsAway) {
printAt(x + 1, y + 1, "?", CENTER, MIDDLE);
} else {
char hopStr[4];
snprintf(hopStr, sizeof(hopStr), "%d", m.hopsAway);
printAt(x, y + 1, hopStr, CENTER, MIDDLE);
}
// Restore default font and color
setFont(fontSmall);
setTextColor(BLACK);
}
// Dual map scale bars
int16_t horizPx = width() * 0.25f;
int16_t vertPx = height() * 0.25f;
float horizMeters = horizPx / metersToPx;
float vertMeters = vertPx / metersToPx;
auto formatDistance = [&](float meters, char *out, size_t len) {
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
float feet = meters * 3.28084f;
if (feet < 528)
snprintf(out, len, "%.0f ft", feet);
else {
float miles = feet / 5280.0f;
snprintf(out, len, miles < 10 ? "%.1f mi" : "%.0f mi", miles);
}
} else {
if (meters >= 1000)
snprintf(out, len, "%.1f km", meters / 1000.0f);
else
snprintf(out, len, "%.0f m", meters);
}
};
// Horizontal scale bar
int16_t horizBarY = height() - 2;
int16_t horizBarX = 1;
drawLine(horizBarX, horizBarY, horizBarX + horizPx, horizBarY, BLACK);
drawLine(horizBarX, horizBarY - 3, horizBarX, horizBarY + 3, BLACK);
drawLine(horizBarX + horizPx, horizBarY - 3, horizBarX + horizPx, horizBarY + 3, BLACK);
char horizLabel[32];
formatDistance(horizMeters, horizLabel, sizeof(horizLabel));
int16_t horizLabelW = getTextWidth(horizLabel);
int16_t horizLabelH = getFont().lineHeight();
int16_t horizLabelX = horizBarX + horizPx + 4;
int16_t horizLabelY = horizBarY - horizLabelH + 1;
fillRect(horizLabelX - 2, horizLabelY - 1, horizLabelW + 4, horizLabelH + 2, WHITE);
printAt(horizLabelX, horizBarY, horizLabel, LEFT, BOTTOM);
// Vertical scale bar
int16_t vertBarX = 1;
int16_t vertBarBottom = horizBarY;
int16_t vertBarTop = vertBarBottom - vertPx;
drawLine(vertBarX, vertBarBottom, vertBarX, vertBarTop, BLACK);
drawLine(vertBarX - 3, vertBarBottom, vertBarX + 3, vertBarBottom, BLACK);
drawLine(vertBarX - 3, vertBarTop, vertBarX + 3, vertBarTop, BLACK);
char vertTopLabel[32];
formatDistance(vertMeters, vertTopLabel, sizeof(vertTopLabel));
int16_t topLabelY = vertBarTop - getFont().lineHeight() - 2;
int16_t topLabelW = getTextWidth(vertTopLabel);
int16_t topLabelH = getFont().lineHeight();
fillRect(vertBarX - 2, topLabelY - 1, topLabelW + 6, topLabelH + 2, WHITE);
printAt(vertBarX + (topLabelW / 2) + 1, topLabelY + (topLabelH / 2), vertTopLabel, CENTER, MIDDLE);
char vertBottomLabel[32];
formatDistance(vertMeters, vertBottomLabel, sizeof(vertBottomLabel));
int16_t bottomLabelY = vertBarBottom + 4;
int16_t bottomLabelW = getTextWidth(vertBottomLabel);
int16_t bottomLabelH = getFont().lineHeight();
fillRect(vertBarX - 2, bottomLabelY - 1, bottomLabelW + 6, bottomLabelH + 2, WHITE);
printAt(vertBarX + (bottomLabelW / 2) + 1, bottomLabelY + (bottomLabelH / 2), vertBottomLabel, CENTER, MIDDLE);
// Draw our node LAST with full white fill + outline
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
if (ourNode && nodeDB->hasValidPosition(ourNode)) {
Marker self = calculateMarker(ourNode->position.latitude_i * 1e-7, ourNode->position.longitude_i * 1e-7, false, 0);
int16_t centerX = X(0.5) + (self.eastMeters * metersToPx);
int16_t centerY = Y(0.5) - (self.northMeters * metersToPx);
// White fill background + halo
fillCircle(centerX, centerY, 8, WHITE); // big white base
drawCircle(centerX, centerY, 8, WHITE); // crisp edge
// Black bullseye on top
drawCircle(centerX, centerY, 6, BLACK);
fillCircle(centerX, centerY, 2, BLACK);
// Crosshairs
drawLine(centerX - 8, centerY, centerX + 8, centerY, BLACK);
drawLine(centerX, centerY - 8, centerX, centerY + 8, BLACK);
} }
} }
@@ -63,116 +165,129 @@ void InkHUD::MapApplet::onRender()
void InkHUD::MapApplet::getMapCenter(float *lat, float *lng) void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
{ {
// Find mean lat long coords // If we have a valid position for our own node, use that as the anchor
// ============================ meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
// - assigning X, Y and Z values to position on Earth's surface in 3D space, relative to center of planet if (ourNode && nodeDB->hasValidPosition(ourNode)) {
// - averages the x, y and z coords *lat = ourNode->position.latitude_i * 1e-7;
// - uses tan to find angles for lat / long degrees *lng = ourNode->position.longitude_i * 1e-7;
// - longitude: triangle formed by x and y (on plane of the equator) } else {
// - latitude: triangle formed by z (north south), // Find mean lat long coords
// and the line along plane of equator which stretches from earth's axis to where point xyz intersects planet's surface // ============================
// - assigning X, Y and Z values to position on Earth's surface in 3D space, relative to center of planet
// - averages the x, y and z coords
// - uses tan to find angles for lat / long degrees
// - longitude: triangle formed by x and y (on plane of the equator)
// - latitude: triangle formed by z (north south),
// and the line along plane of equator which stretches from earth's axis to where point xyz intersects planet's
// surface
// Working totals, averaged after nodeDB processed // Working totals, averaged after nodeDB processed
uint32_t positionCount = 0; uint32_t positionCount = 0;
float xAvg = 0; float xAvg = 0;
float yAvg = 0; float yAvg = 0;
float zAvg = 0; float zAvg = 0;
// For each node in db // For each node in db
for (uint32_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { for (uint32_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
// Skip if no position // Skip if no position
if (!nodeDB->hasValidPosition(node)) if (!nodeDB->hasValidPosition(node))
continue; continue;
// Skip if derived applet doesn't want to show this node on the map // Skip if derived applet doesn't want to show this node on the map
if (!shouldDrawNode(node)) if (!shouldDrawNode(node))
continue; continue;
// Latitude and Longitude of node, in radians // Latitude and Longitude of node, in radians
float latRad = node->position.latitude_i * (1e-7) * DEG_TO_RAD; float latRad = node->position.latitude_i * (1e-7) * DEG_TO_RAD;
float lngRad = node->position.longitude_i * (1e-7) * DEG_TO_RAD; float lngRad = node->position.longitude_i * (1e-7) * DEG_TO_RAD;
// Convert to cartesian points, with center of earth at 0, 0, 0 // Convert to cartesian points, with center of earth at 0, 0, 0
// Exact distance from center is irrelevant, as we're only interested in the vector // Exact distance from center is irrelevant, as we're only interested in the vector
float x = cos(latRad) * cos(lngRad); float x = cos(latRad) * cos(lngRad);
float y = cos(latRad) * sin(lngRad); float y = cos(latRad) * sin(lngRad);
float z = sin(latRad); float z = sin(latRad);
// To find mean values shortly // To find mean values shortly
xAvg += x; xAvg += x;
yAvg += y; yAvg += y;
zAvg += z; zAvg += z;
positionCount++; positionCount++;
}
// All NodeDB processed, find mean values
xAvg /= positionCount;
yAvg /= positionCount;
zAvg /= positionCount;
// Longitude from cartesian coords
// (Angle from 3D coords describing a point of globe's surface)
/*
UK
/-------\
(Top View) /- -\
/- (You) -\
/- . -\
/- . X -\
Asia - ... - USA
\- Y -/
\- -/
\- -/
\- -/
\- -----/
Pacific
*/
*lng = atan2(yAvg, xAvg) * RAD_TO_DEG;
// Latitude from cartesian coords
// (Angle from 3D coords describing a point on the globe's surface)
// As latitude increases, distance from the Earth's north-south axis out to our surface point decreases.
// Means we need to first find the hypotenuse which becomes base of our triangle in the second step
/*
UK North
/-------\ (Front View) /-------\
(Top View) /- -\ /- -\
/- (You) -\ /-(You) -\
/- /. -\ /- . -\
/- √X²+Y²/ . X -\ /- Z . -\
Asia - /... - USA - ..... -
\- Y -/ \- √X²+Y² -/
\- -/ \- -/
\- -/ \- -/
\- -/ \- -/
\- -----/ \- -----/
Pacific South
*/
float hypotenuse = sqrt((xAvg * xAvg) + (yAvg * yAvg)); // Distance from globe's north-south axis to surface intersect
*lat = atan2(zAvg, hypotenuse) * RAD_TO_DEG;
} }
// All NodeDB processed, find mean values // Use either our node position, or the mean fallback as the center
xAvg /= positionCount; latCenter = *lat;
yAvg /= positionCount; lngCenter = *lng;
zAvg /= positionCount;
// Longitude from cartesian coords
// (Angle from 3D coords describing a point of globe's surface)
/*
UK
/-------\
(Top View) /- -\
/- (You) -\
/- . -\
/- . X -\
Asia - ... - USA
\- Y -/
\- -/
\- -/
\- -/
\- -----/
Pacific
*/
*lng = atan2(yAvg, xAvg) * RAD_TO_DEG;
// Latitude from cartesian coords
// (Angle from 3D coords describing a point on the globe's surface)
// As latitude increases, distance from the Earth's north-south axis out to our surface point decreases.
// Means we need to first find the hypotenuse which becomes base of our triangle in the second step
/*
UK North
/-------\ (Front View) /-------\
(Top View) /- -\ /- -\
/- (You) -\ /-(You) -\
/- /. -\ /- . -\
/- √X²+Y²/ . X -\ /- Z . -\
Asia - /... - USA - ..... -
\- Y -/ \- √X²+Y² -/
\- -/ \- -/
\- -/ \- -/
\- -/ \- -/
\- -----/ \- -----/
Pacific South
*/
float hypotenuse = sqrt((xAvg * xAvg) + (yAvg * yAvg)); // Distance from globe's north-south axis to surface intersect
*lat = atan2(zAvg, hypotenuse) * RAD_TO_DEG;
// ---------------------------------------------- // ----------------------------------------------
// This has given us the "mean position" // This has given us either:
// This will be a position *somewhere* near the center of our nodes. // - our actual position (preferred), or
// What we actually want is to place our center so that our outermost nodes end up on the border of our map. // - a mean position (fallback if we had no fix)
// The only real use of our "mean position" is to give us a reference frame: //
// which direction is east, and which is west. // What we actually want is to place our center so that our outermost nodes
// end up on the border of our map. The only real use of our "center" is to give
// us a reference frame: which direction is east, and which is west.
//------------------------------------------------ //------------------------------------------------
// Find furthest nodes from "mean lat long" // Find furthest nodes from our center
// ======================================== // ========================================
float northernmost = latCenter; float northernmost = latCenter;
float southernmost = latCenter; float southernmost = latCenter;
float easternmost = lngCenter; float easternmost = lngCenter;
float westernmost = lngCenter; float westernmost = lngCenter;
for (uint8_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
// Skip if no position // Skip if no position
@@ -184,14 +299,14 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
continue; continue;
// Check for a new top or bottom latitude // Check for a new top or bottom latitude
float lat = node->position.latitude_i * 1e-7; float latNode = node->position.latitude_i * 1e-7;
northernmost = max(northernmost, lat); northernmost = max(northernmost, latNode);
southernmost = min(southernmost, lat); southernmost = min(southernmost, latNode);
// Longitude is trickier // Longitude is trickier
float lng = node->position.longitude_i * 1e-7; float lngNode = node->position.longitude_i * 1e-7;
float degEastward = fmod(((lng - lngCenter) + 360), 360); // Degrees traveled east from lngCenter to reach node float degEastward = fmod(((lngNode - lngCenter) + 360), 360); // Degrees traveled east from lngCenter to reach node
float degWestward = abs(fmod(((lng - lngCenter) - 360), 360)); // Degrees traveled west from lngCenter to reach node float degWestward = abs(fmod(((lngNode - lngCenter) - 360), 360)); // Degrees traveled west from lngCenter to reach node
if (degEastward < degWestward) if (degEastward < degWestward)
easternmost = max(easternmost, lngCenter + degEastward); easternmost = max(easternmost, lngCenter + degEastward);
else else
@@ -250,7 +365,6 @@ InkHUD::MapApplet::Marker InkHUD::MapApplet::calculateMarker(float lat, float ln
m.hopsAway = hopsAway; m.hopsAway = hopsAway;
return m; return m;
} }
// Draw a marker on the map for a node, with a shortname label, and backing box // Draw a marker on the map for a node, with a shortname label, and backing box
void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node) void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node)
{ {
@@ -324,6 +438,18 @@ void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node)
textX = labelX + paddingW; textX = labelX + paddingW;
} }
// Prevent overlap with scale bars and their labels
// Define a "safe zone" in the bottom-left where the scale bars and text are drawn
constexpr int16_t safeZoneHeight = 28; // adjust based on your label font height
constexpr int16_t safeZoneWidth = 60; // adjust based on horizontal label width zone
bool overlapsScale = (labelY + labelH > height() - safeZoneHeight) && (labelX < safeZoneWidth);
// If it overlaps, shift label upward slightly above the safe zone
if (overlapsScale) {
labelY = height() - safeZoneHeight - labelH - 2;
textY = labelY + (labelH / 2);
}
// Backing box // Backing box
fillRect(labelX, labelY, labelW, labelH, WHITE); fillRect(labelX, labelY, labelW, labelH, WHITE);
drawRect(labelX, labelY, labelW, labelH, BLACK); drawRect(labelX, labelY, labelW, labelH, BLACK);
@@ -348,8 +474,8 @@ void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node)
// Need at least two, to draw a sensible map // Need at least two, to draw a sensible map
bool InkHUD::MapApplet::enoughMarkers() bool InkHUD::MapApplet::enoughMarkers()
{ {
uint8_t count = 0; size_t count = 0;
for (uint8_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i); meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
// Count nodes // Count nodes

View File

@@ -127,6 +127,11 @@ void InkHUD::NodeListApplet::onRender()
// Y value (top) of the current card. Increases as we draw. // Y value (top) of the current card. Increases as we draw.
uint16_t cardTopY = headerDivY + padDivH; uint16_t cardTopY = headerDivY + padDivH;
// Clean up deleted nodes before drawing
cards.erase(
std::remove_if(cards.begin(), cards.end(), [](const CardInfo &c) { return nodeDB->getMeshNode(c.nodeNum) == nullptr; }),
cards.end());
// -- Each node in list -- // -- Each node in list --
for (auto card = cards.begin(); card != cards.end(); ++card) { for (auto card = cards.begin(); card != cards.end(); ++card) {
@@ -141,6 +146,11 @@ void InkHUD::NodeListApplet::onRender()
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum); meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum);
// Skip deleted nodes
if (!node) {
continue;
}
// -- Shortname -- // -- Shortname --
// Parse special chars in the short name // Parse special chars in the short name
// Use "?" if unknown // Use "?" if unknown
@@ -188,7 +198,7 @@ void InkHUD::NodeListApplet::onRender()
drawSignalIndicator(signalX, signalY, signalW, signalH, signal); drawSignalIndicator(signalX, signalY, signalW, signalH, signal);
} }
// Otherwise, print "hops away" info, if available // Otherwise, print "hops away" info, if available
else if (hopsAway != CardInfo::HOPS_UNKNOWN) { else if (hopsAway != CardInfo::HOPS_UNKNOWN && node) {
std::string hopString = to_string(node->hops_away); std::string hopString = to_string(node->hops_away);
hopString += " Hop"; hopString += " Hop";
if (node->hops_away != 1) if (node->hops_away != 1)

View File

@@ -709,7 +709,7 @@ void InkHUD::MenuApplet::drawSystemInfoPanel(int16_t left, int16_t top, uint16_t
// Voltage // Voltage
float voltage = powerStatus->getBatteryVoltageMv() / 1000.0; float voltage = powerStatus->getBatteryVoltageMv() / 1000.0;
char voltageStr[6]; // "XX.XV" char voltageStr[6]; // "XX.XV"
sprintf(voltageStr, "%.1fV", voltage); sprintf(voltageStr, "%.2fV", voltage);
printAt(colC[0], labelT, "Bat", CENTER, TOP); printAt(colC[0], labelT, "Bat", CENTER, TOP);
printAt(colC[0], valT, voltageStr, CENTER, TOP); printAt(colC[0], valT, voltageStr, CENTER, TOP);

View File

@@ -5,7 +5,6 @@ A pattern / collection of resources for creating custom UIs, to target small gro
For an example, see the `heltec-vision-master-e290-inkhud` platformio env. For an example, see the `heltec-vision-master-e290-inkhud` platformio env.
- platformio.ini - platformio.ini
- suppress default Meshtastic components (Screen, ButtonThread, etc) - suppress default Meshtastic components (Screen, ButtonThread, etc)
- define `MESHTASTIC_INCLUDE_NICHE_GRAPHICS` - define `MESHTASTIC_INCLUDE_NICHE_GRAPHICS`
- (possibly) Edit `build_src_filter` to include our new nicheGraphics.h file - (possibly) Edit `build_src_filter` to include our new nicheGraphics.h file

View File

@@ -0,0 +1,217 @@
#if defined(HACKADAY_COMMUNICATOR)
#include "HackadayCommunicatorKeyboard.h"
#include "main.h"
#define _TCA8418_COLS 10
#define _TCA8418_ROWS 8
#define _TCA8418_NUM_KEYS 80
#define _TCA8418_MULTI_TAP_THRESHOLD 1500
using Key = TCA8418KeyboardBase::TCA8418Key;
constexpr uint8_t modifierRightShiftKey = 30;
constexpr uint8_t modifierRightShift = 0b0001;
constexpr uint8_t modifierLeftShiftKey = 76; // keynum -1
constexpr uint8_t modifierLeftShift = 0b0001;
// constexpr uint8_t modifierSymKey = 42;
// constexpr uint8_t modifierSym = 0b0010;
// Num chars per key, Modulus for rotating through characters
static uint8_t HackadayCommunicatorTapMod[_TCA8418_NUM_KEYS] = {
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 2, 2, 2, 1, 2, 2, 0, 0, 0, 2, 1, 2, 2, 0, 1, 1, 0,
};
static unsigned char HackadayCommunicatorTapMap[_TCA8418_NUM_KEYS][2] = {{},
{},
{'+'},
{'9'},
{'8'},
{'7'},
{'2'},
{'3'},
{'4'},
{'5'},
{Key::ESC},
{'q', 'Q'},
{'w', 'W'},
{'e', 'E'},
{'r', 'R'},
{'t', 'T'},
{'y', 'Y'},
{'u', 'U'},
{'i', 'I'},
{'o', 'O'},
{Key::TAB},
{'a', 'A'},
{'s', 'S'},
{'d', 'D'},
{'f', 'F'},
{'g', 'G'},
{'h', 'H'},
{'j', 'J'},
{'k', 'K'},
{'l', 'L'},
{},
{'z', 'Z'},
{'x', 'X'},
{'c', 'C'},
{'v', 'V'},
{'b', 'B'},
{'n', 'N'},
{'m', 'M'},
{',', '<'},
{'.', '>'},
{},
{},
{},
{'\\'},
{' '},
{},
{Key::RIGHT},
{Key::DOWN},
{Key::LEFT},
{},
{},
{},
{'-'},
{'6', '^'},
{'5', '%'},
{'4', '$'},
{'[', '{'},
{']', '}'},
{'p', 'P'},
{},
{},
{},
{'*'},
{'3', '#'},
{'2', '@'},
{'1', '!'},
{Key::SELECT},
{'\'', '"'},
{';', ':'},
{},
{},
{},
{'/', '?'},
{'='},
{'.', '>'},
{'0', ')'},
{},
{Key::UP},
{Key::BSP},
{}};
HackadayCommunicatorKeyboard::HackadayCommunicatorKeyboard()
: TCA8418KeyboardBase(_TCA8418_ROWS, _TCA8418_COLS), modifierFlag(0), last_modifier_time(0), last_key(-1), next_key(-1),
last_tap(0L), char_idx(0), tap_interval(0)
{
reset();
}
void HackadayCommunicatorKeyboard::reset(void)
{
TCA8418KeyboardBase::reset();
enableInterrupts();
}
// handle multi-key presses (shift and alt)
void HackadayCommunicatorKeyboard::trigger()
{
uint8_t count = keyCount();
if (count == 0)
return;
for (uint8_t i = 0; i < count; ++i) {
uint8_t k = readRegister(TCA8418_REG_KEY_EVENT_A + i);
uint8_t key = k & 0x7F;
if (k & 0x80) {
pressed(key);
} else {
released();
state = Idle;
}
}
}
void HackadayCommunicatorKeyboard::pressed(uint8_t key)
{
if (state == Init || state == Busy) {
return;
}
if (modifierFlag && (millis() - last_modifier_time > _TCA8418_MULTI_TAP_THRESHOLD)) {
modifierFlag = 0;
}
uint8_t next_key = 0;
int row = (key - 1) / 10;
int col = (key - 1) % 10;
if (row >= _TCA8418_ROWS || col >= _TCA8418_COLS) {
return; // Invalid key
}
next_key = row * _TCA8418_COLS + col;
state = Held;
uint32_t now = millis();
tap_interval = now - last_tap;
updateModifierFlag(next_key);
if (isModifierKey(next_key)) {
last_modifier_time = now;
}
if (tap_interval < 0) {
last_tap = 0;
state = Busy;
return;
}
if (next_key != last_key || tap_interval > _TCA8418_MULTI_TAP_THRESHOLD) {
char_idx = 0;
} else {
char_idx += 1;
}
last_key = next_key;
last_tap = now;
}
void HackadayCommunicatorKeyboard::released()
{
if (state != Held) {
return;
}
if (last_key < 0 || last_key >= _TCA8418_NUM_KEYS) {
last_key = -1;
state = Idle;
return;
}
uint32_t now = millis();
last_tap = now;
if (HackadayCommunicatorTapMod[last_key])
queueEvent(HackadayCommunicatorTapMap[last_key][modifierFlag % HackadayCommunicatorTapMod[last_key]]);
if (isModifierKey(last_key) == false)
modifierFlag = 0;
}
void HackadayCommunicatorKeyboard::updateModifierFlag(uint8_t key)
{
if (key == modifierRightShiftKey) {
modifierFlag ^= modifierRightShift;
} else if (key == modifierLeftShiftKey) {
modifierFlag ^= modifierLeftShift;
}
}
bool HackadayCommunicatorKeyboard::isModifierKey(uint8_t key)
{
return (key == modifierRightShiftKey || key == modifierLeftShiftKey);
}
#endif

View File

@@ -0,0 +1,26 @@
#include "TCA8418KeyboardBase.h"
class HackadayCommunicatorKeyboard : public TCA8418KeyboardBase
{
public:
HackadayCommunicatorKeyboard();
void reset(void);
void trigger(void) override;
virtual ~HackadayCommunicatorKeyboard() {}
protected:
void pressed(uint8_t key) override;
void released(void) override;
void updateModifierFlag(uint8_t key);
bool isModifierKey(uint8_t key);
private:
uint8_t modifierFlag; // Flag to indicate if a modifier key is pressed
uint32_t last_modifier_time; // Timestamp of the last modifier key press
int8_t last_key;
int8_t next_key;
uint32_t last_tap;
uint8_t char_idx;
int32_t tap_interval;
};

View File

@@ -1,11 +1,13 @@
#include "InputBroker.h" #include "InputBroker.h"
#include "PowerFSM.h" // needed for event trigger #include "PowerFSM.h" // needed for event trigger
#include "configuration.h"
#include "modules/ExternalNotificationModule.h"
InputBroker *inputBroker = nullptr; InputBroker *inputBroker = nullptr;
InputBroker::InputBroker() InputBroker::InputBroker()
{ {
#ifdef HAS_FREE_RTOS #if defined(HAS_FREE_RTOS) && !defined(ARCH_RP2040)
inputEventQueue = xQueueCreate(5, sizeof(InputEvent)); inputEventQueue = xQueueCreate(5, sizeof(InputEvent));
pollSoonQueue = xQueueCreate(5, sizeof(InputPollable *)); pollSoonQueue = xQueueCreate(5, sizeof(InputPollable *));
xTaskCreate(pollSoonWorker, "input-pollSoon", 2 * 1024, this, 10, &pollSoonTask); xTaskCreate(pollSoonWorker, "input-pollSoon", 2 * 1024, this, 10, &pollSoonTask);
@@ -17,7 +19,7 @@ void InputBroker::registerSource(Observable<const InputEvent *> *source)
this->inputEventObserver.observe(source); this->inputEventObserver.observe(source);
} }
#ifdef HAS_FREE_RTOS #if defined(HAS_FREE_RTOS) && !defined(ARCH_RP2040)
void InputBroker::requestPollSoon(InputPollable *pollable) void InputBroker::requestPollSoon(InputPollable *pollable)
{ {
if (xPortInIsrContext() == pdTRUE) { if (xPortInIsrContext() == pdTRUE) {
@@ -48,11 +50,17 @@ void InputBroker::processInputEventQueue()
int InputBroker::handleInputEvent(const InputEvent *event) int InputBroker::handleInputEvent(const InputEvent *event)
{ {
powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release powerFSM.trigger(EVENT_INPUT); // todo: not every input should wake, like long hold release
if (event && event->inputEvent != INPUT_BROKER_NONE && externalNotificationModule &&
moduleConfig.external_notification.enabled && externalNotificationModule->nagging()) {
externalNotificationModule->stopNow();
}
this->notifyObservers(event); this->notifyObservers(event);
return 0; return 0;
} }
#ifdef HAS_FREE_RTOS #if defined(HAS_FREE_RTOS) && !defined(ARCH_RP2040)
void InputBroker::pollSoonWorker(void *p) void InputBroker::pollSoonWorker(void *p)
{ {
InputBroker *instance = (InputBroker *)p; InputBroker *instance = (InputBroker *)p;

View File

@@ -3,6 +3,12 @@
#include "Observer.h" #include "Observer.h"
#include "freertosinc.h" #include "freertosinc.h"
#ifdef InputBrokerDebug
#define LOG_INPUT(...) LOG_DEBUG(__VA_ARGS__)
#else
#define LOG_INPUT(...)
#endif
enum input_broker_event { enum input_broker_event {
INPUT_BROKER_NONE = 0, INPUT_BROKER_NONE = 0,
INPUT_BROKER_SELECT = 10, INPUT_BROKER_SELECT = 10,
@@ -59,7 +65,7 @@ class InputBroker : public Observable<const InputEvent *>
InputBroker(); InputBroker();
void registerSource(Observable<const InputEvent *> *source); void registerSource(Observable<const InputEvent *> *source);
void injectInputEvent(const InputEvent *event) { handleInputEvent(event); } void injectInputEvent(const InputEvent *event) { handleInputEvent(event); }
#ifdef HAS_FREE_RTOS #if defined(HAS_FREE_RTOS) && !defined(ARCH_RP2040)
void requestPollSoon(InputPollable *pollable); void requestPollSoon(InputPollable *pollable);
void queueInputEvent(const InputEvent *event); void queueInputEvent(const InputEvent *event);
void processInputEventQueue(); void processInputEventQueue();
@@ -69,7 +75,7 @@ class InputBroker : public Observable<const InputEvent *>
int handleInputEvent(const InputEvent *event); int handleInputEvent(const InputEvent *event);
private: private:
#ifdef HAS_FREE_RTOS #if defined(HAS_FREE_RTOS) && !defined(ARCH_RP2040)
QueueHandle_t inputEventQueue; QueueHandle_t inputEventQueue;
QueueHandle_t pollSoonQueue; QueueHandle_t pollSoonQueue;
TaskHandle_t pollSoonTask; TaskHandle_t pollSoonTask;

View File

@@ -2,6 +2,8 @@
#include "configuration.h" #include "configuration.h"
#include <Throttle.h> #include <Throttle.h>
SerialKeyboard *globalSerialKeyboard = nullptr;
#ifdef INPUTBROKER_SERIAL_TYPE #ifdef INPUTBROKER_SERIAL_TYPE
#define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file #define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file
@@ -25,6 +27,8 @@ unsigned char KeyMap[3][4][10] = {{{'.', 'a', 'd', 'g', 'j', 'm', 'p', 't', 'w',
SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name) SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name)
{ {
this->_originName = name; this->_originName = name;
globalSerialKeyboard = this;
} }
void SerialKeyboard::erase() void SerialKeyboard::erase()
@@ -85,9 +89,21 @@ int32_t SerialKeyboard::runOnce()
e.source = this->_originName; e.source = this->_originName;
// SELECT OR SEND OR CANCEL EVENT // SELECT OR SEND OR CANCEL EVENT
if (!(shiftRegister2 & (1 << 3))) { if (!(shiftRegister2 & (1 << 3))) {
e.inputEvent = INPUT_BROKER_UP; if (shift > 0) {
e.inputEvent = INPUT_BROKER_ANYKEY; // REQUIRED
e.kbchar = 0x09; // TAB
shift = 0; // reset shift after TAB
} else {
e.inputEvent = INPUT_BROKER_LEFT;
}
} else if (!(shiftRegister2 & (1 << 2))) { } else if (!(shiftRegister2 & (1 << 2))) {
e.inputEvent = INPUT_BROKER_RIGHT; if (shift > 0) {
e.inputEvent = INPUT_BROKER_ANYKEY; // REQUIRED
e.kbchar = 0x09; // TAB
shift = 0; // reset shift after TAB
} else {
e.inputEvent = INPUT_BROKER_RIGHT;
}
e.kbchar = 0; e.kbchar = 0;
} else if (!(shiftRegister2 & (1 << 1))) { } else if (!(shiftRegister2 & (1 << 1))) {
e.inputEvent = INPUT_BROKER_SELECT; e.inputEvent = INPUT_BROKER_SELECT;

View File

@@ -8,6 +8,8 @@ class SerialKeyboard : public Observable<const InputEvent *>, public concurrency
public: public:
explicit SerialKeyboard(const char *name); explicit SerialKeyboard(const char *name);
uint8_t getShift() const { return shift; }
protected: protected:
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
void erase(); void erase();
@@ -22,4 +24,6 @@ class SerialKeyboard : public Observable<const InputEvent *>, public concurrency
int lastKeyPressed = 13; int lastKeyPressed = 13;
int quickPress = 0; int quickPress = 0;
unsigned long lastPressTime = 0; unsigned long lastPressTime = 0;
}; };
extern SerialKeyboard *globalSerialKeyboard;

View File

@@ -57,7 +57,7 @@ static unsigned char TDeckProTapMap[_TCA8418_NUM_KEYS][5] = {
{0x00, 0x00, 0x00}, {0x00, 0x00, 0x00},
{0x00, 0x00, 0x00}, {0x00, 0x00, 0x00},
{0x20, 0x00, 0x00}, {0x20, 0x00, 0x00},
{0x00, 0x00, 0x00}, {0x00, 0x00, '0'},
{0x00, 0x00, 0x00} // R_Shift, sym, space, mic, L_Shift {0x00, 0x00, 0x00} // R_Shift, sym, space, mic, L_Shift
}; };

View File

@@ -7,6 +7,8 @@
#include "TDeckProKeyboard.h" #include "TDeckProKeyboard.h"
#elif defined(T_LORA_PAGER) #elif defined(T_LORA_PAGER)
#include "TLoraPagerKeyboard.h" #include "TLoraPagerKeyboard.h"
#elif defined(HACKADAY_COMMUNICATOR)
#include "HackadayCommunicatorKeyboard.h"
#else #else
#include "TCA8418Keyboard.h" #include "TCA8418Keyboard.h"
#endif #endif
@@ -20,6 +22,8 @@ KbI2cBase::KbI2cBase(const char *name)
TCAKeyboard(*(new TDeckProKeyboard())) TCAKeyboard(*(new TDeckProKeyboard()))
#elif defined(T_LORA_PAGER) #elif defined(T_LORA_PAGER)
TCAKeyboard(*(new TLoraPagerKeyboard())) TCAKeyboard(*(new TLoraPagerKeyboard()))
#elif defined(HACKADAY_COMMUNICATOR)
TCAKeyboard(*(new HackadayCommunicatorKeyboard()))
#else #else
TCAKeyboard(*(new TCA8418Keyboard())) TCAKeyboard(*(new TCA8418Keyboard()))
#endif #endif
@@ -328,7 +332,7 @@ int32_t KbI2cBase::runOnce()
break; break;
} }
if (e.inputEvent != INPUT_BROKER_NONE) { if (e.inputEvent != INPUT_BROKER_NONE) {
LOG_DEBUG("TCA8418 Notifying: %i Char: %c", e.inputEvent, e.kbchar); // LOG_DEBUG("TCA8418 Notifying: %i Char: %c", e.inputEvent, e.kbchar);
this->notifyObservers(&e); this->notifyObservers(&e);
} }
TCAKeyboard.trigger(); TCAKeyboard.trigger();

View File

@@ -394,7 +394,10 @@ void setup()
io.pinMode(EXPANDS_GPIO_EN, OUTPUT); io.pinMode(EXPANDS_GPIO_EN, OUTPUT);
io.digitalWrite(EXPANDS_GPIO_EN, HIGH); io.digitalWrite(EXPANDS_GPIO_EN, HIGH);
io.pinMode(EXPANDS_SD_PULLEN, INPUT); io.pinMode(EXPANDS_SD_PULLEN, INPUT);
#elif defined(HACKADAY_COMMUNICATOR)
pinMode(KB_INT, INPUT);
#endif #endif
concurrency::hasBeenSetup = true; concurrency::hasBeenSetup = true;
#if ARCH_PORTDUINO #if ARCH_PORTDUINO
SPISettings spiSettings(portduino_config.spiSpeed, MSBFIRST, SPI_MODE0); SPISettings spiSettings(portduino_config.spiSpeed, MSBFIRST, SPI_MODE0);
@@ -436,6 +439,12 @@ void setup()
LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n"); LOG_INFO("\n\n//\\ E S H T /\\ S T / C\n");
#if defined(DEBUG_MUTE) && defined(DEBUG_PORT)
DEBUG_PORT.printf("\r\n\r\n//\\ E S H T /\\ S T / C\r\n");
DEBUG_PORT.printf("Version %s for %s from %s\r\n", optstr(APP_VERSION), optstr(APP_ENV), optstr(APP_REPO));
DEBUG_PORT.printf("Debug mute is enabled, there will be no serial output.\r\n");
#endif
initDeepSleep(); initDeepSleep();
#if defined(MODEM_POWER_EN) #if defined(MODEM_POWER_EN)
@@ -471,6 +480,10 @@ void setup()
#ifdef RESET_OLED #ifdef RESET_OLED
pinMode(RESET_OLED, OUTPUT); pinMode(RESET_OLED, OUTPUT);
digitalWrite(RESET_OLED, 1); digitalWrite(RESET_OLED, 1);
delay(2);
digitalWrite(RESET_OLED, 0);
delay(10);
digitalWrite(RESET_OLED, 1);
#endif #endif
#ifdef SENSOR_POWER_CTRL_PIN #ifdef SENSOR_POWER_CTRL_PIN
@@ -841,7 +854,14 @@ void setup()
SPI.begin(); SPI.begin();
} }
#elif !defined(ARCH_ESP32) // ARCH_RP2040 #elif !defined(ARCH_ESP32) // ARCH_RP2040
#if defined(RAK3401) || defined(RAK13302)
pinMode(WB_IO2, OUTPUT);
digitalWrite(WB_IO2, HIGH);
SPI1.setPins(LORA_MISO, LORA_SCK, LORA_MOSI);
SPI1.begin();
#else
SPI.begin(); SPI.begin();
#endif
#else #else
// ESP32 // ESP32
#if defined(HW_SPI1_DEVICE) #if defined(HW_SPI1_DEVICE)
@@ -861,7 +881,7 @@ void setup()
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \ #if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \ defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
defined(USE_SPISSD1306) defined(USE_SPISSD1306) || defined(USE_ST7796) || defined(HACKADAY_COMMUNICATOR)
screen = new graphics::Screen(screen_found, screen_model, screen_geometry); screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
#elif defined(ARCH_PORTDUINO) #elif defined(ARCH_PORTDUINO)
if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || portduino_config.displayPanel) && if ((screen_found.port != ScanI2C::I2CPort::NO_I2C || portduino_config.displayPanel) &&
@@ -946,6 +966,7 @@ void setup()
i2cScanner.reset(); i2cScanner.reset();
#endif #endif
#if !defined(MESHTASTIC_EXCLUDE_PKI)
// warn the user about a low entropy key // warn the user about a low entropy key
if (nodeDB->keyIsLowEntropy && !nodeDB->hasWarned) { if (nodeDB->keyIsLowEntropy && !nodeDB->hasWarned) {
LOG_WARN(LOW_ENTROPY_WARNING); LOG_WARN(LOW_ENTROPY_WARNING);
@@ -956,6 +977,7 @@ void setup()
service->sendClientNotification(cn); service->sendClientNotification(cn);
nodeDB->hasWarned = true; nodeDB->hasWarned = true;
} }
#endif
// buttons are now inputBroker, so have to come after setupModules // buttons are now inputBroker, so have to come after setupModules
#if HAS_BUTTON #if HAS_BUTTON
@@ -1136,7 +1158,7 @@ void setup()
// the current region name) // the current region name)
#if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \ #if defined(ST7701_CS) || defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || \
defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \ defined(ST7789_CS) || defined(HX8357_CS) || defined(USE_ST7789) || defined(ILI9488_CS) || defined(ST7796_CS) || \
defined(USE_SPISSD1306) defined(USE_ST7796) || defined(USE_SPISSD1306) || defined(HACKADAY_COMMUNICATOR)
if (screen) if (screen)
screen->setup(); screen->setup();
#elif defined(ARCH_PORTDUINO) #elif defined(ARCH_PORTDUINO)
@@ -1388,7 +1410,7 @@ void setup()
#endif #endif
// check if the radio chip matches the selected region // check if the radio chip matches the selected region
if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())) { if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && rIf && (!rIf->wideLora())) {
LOG_WARN("LoRa chip does not support 2.4GHz. Revert to unset"); LOG_WARN("LoRa chip does not support 2.4GHz. Revert to unset");
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET; config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
nodeDB->saveToDisk(SEGMENT_CONFIG); nodeDB->saveToDisk(SEGMENT_CONFIG);
@@ -1582,7 +1604,7 @@ void loop()
#endif #endif
service->loop(); service->loop();
#if !MESHTASTIC_EXCLUDE_INPUTBROKER && defined(HAS_FREE_RTOS) #if !MESHTASTIC_EXCLUDE_INPUTBROKER && defined(HAS_FREE_RTOS) && !defined(ARCH_RP2040)
if (inputBroker) if (inputBroker)
inputBroker->processInputEventQueue(); inputBroker->processInputEventQueue();
#endif #endif

View File

@@ -4,17 +4,22 @@
#if !(MESHTASTIC_EXCLUDE_PKI) #if !(MESHTASTIC_EXCLUDE_PKI)
#include "NodeDB.h" #include "NodeDB.h"
#include "XEdDSA.h"
#include "aes-ccm.h" #include "aes-ccm.h"
#include "meshUtils.h" #include "meshUtils.h"
#include <Crypto.h> #include <Crypto.h>
#include <Curve25519.h> #include <Curve25519.h>
#include <Ed25519.h>
#include <RNG.h> #include <RNG.h>
#include <SHA256.h> #include <SHA256.h>
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
#if !defined(ARCH_STM32WL) #ifndef NUM_LIMBS_256BIT
#define CryptRNG RNG #define NUM_LIMBS_BITS(n) (((n) + sizeof(limb_t) * 8 - 1) / (8 * sizeof(limb_t)))
#define NUM_LIMBS_256BIT NUM_LIMBS_BITS(256)
#endif #endif
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
/** /**
* Create a public/private key pair with Curve25519. * Create a public/private key pair with Curve25519.
* *
@@ -35,6 +40,7 @@ void CryptoEngine::generateKeyPair(uint8_t *pubKey, uint8_t *privKey)
Curve25519::dh1(public_key, private_key); Curve25519::dh1(public_key, private_key);
memcpy(pubKey, public_key, sizeof(public_key)); memcpy(pubKey, public_key, sizeof(public_key));
memcpy(privKey, private_key, sizeof(private_key)); memcpy(privKey, private_key, sizeof(private_key));
XEdDSA::priv_curve_to_ed_keys(private_key, xeddsa_private_key, xeddsa_public_key);
} }
/** /**
@@ -54,12 +60,66 @@ bool CryptoEngine::regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey)
} }
memcpy(private_key, privKey, sizeof(private_key)); memcpy(private_key, privKey, sizeof(private_key));
memcpy(public_key, pubKey, sizeof(public_key)); memcpy(public_key, pubKey, sizeof(public_key));
XEdDSA::priv_curve_to_ed_keys(private_key, xeddsa_private_key, xeddsa_public_key);
} else { } else {
LOG_WARN("X25519 key generation failed due to blank private key"); LOG_WARN("X25519 key generation failed due to blank private key");
return false; return false;
} }
return true; return true;
} }
bool CryptoEngine::xeddsa_sign(uint8_t *message, size_t len, uint8_t *signature)
{
XEdDSA::sign(signature, xeddsa_private_key, xeddsa_public_key, message,
len); // sign will need modified to use the raw secret scalar, and not hash it first.
return true;
}
bool CryptoEngine::xeddsa_verify(uint8_t *pubKey, uint8_t *message, size_t len, uint8_t *signature)
{
uint8_t publicKey[32] = {0};
curve_to_ed_pub(pubKey, publicKey);
return XEdDSA::verify(signature, publicKey, message, len);
}
void CryptoEngine::curve_to_ed_pub(uint8_t *curve_pubkey, uint8_t *ed_pubkey)
{
// Apply the birational map defined in RFC 7748, section 4.1 "Curve25519" to calculate an Ed25519 public
// key from a Curve25519 public key. Because the serialization format of Curve25519 public keys only
// contains the u coordinate, the x coordinate of the corresponding Ed25519 public key can't be uniquely
// calculated as defined by the birational map. The x coordinate is represented in the serialization
// format of Ed25519 public keys only in a single sign bit. This function assumes that the sign bit is
// known to the user and is passed accordingly.
fe u, y;
fe one;
fe u_minus_one, u_plus_one, u_plus_one_inv;
// Parse the Curve25519 public key input as a field element containing the u coordinate. RFC 7748,
// section 5 "The X25519 and X448 Functions", mandates that the most significant bit of the Curve25519
// public key has to be zeroized. This is handled by fe_frombytes internally.
fe_frombytes(u, curve_pubkey);
// Calculate the parameters (u - 1) and (u + 1)
fe_1(one);
fe_sub(u_minus_one, u, one);
fe_add(u_plus_one, u, one);
// Invert u + 1
fe_invert(u_plus_one_inv, u_plus_one);
// Calculate y = (u - 1) * inv(u + 1) (mod p)
fe_mul(y, u_minus_one, u_plus_one_inv);
// Serialize the field element containing the y coordinate to the Ed25519 public key output
fe_tobytes(ed_pubkey, y);
// Set the sign bit to zero
ed_pubkey[31] &= 0x7f;
// need to convert the pubkey y = ( u - 1) * inv( u + 1) (mod p).
}
#endif #endif
void CryptoEngine::clearKeys() void CryptoEngine::clearKeys()
{ {
@@ -92,10 +152,10 @@ bool CryptoEngine::encryptCurve25519(uint32_t toNode, uint32_t fromNode, meshtas
LOG_DEBUG("Node %d or their public_key not found", toNode); LOG_DEBUG("Node %d or their public_key not found", toNode);
return false; return false;
} }
if (!crypto->setDHPublicKey(remotePublic.bytes)) { if (!setDHPublicKey(remotePublic.bytes)) {
return false; return false;
} }
crypto->hash(shared_key, 32); hash(shared_key, 32);
initNonce(fromNode, packetNum, extraNonceTmp); initNonce(fromNode, packetNum, extraNonceTmp);
// Calculate the shared secret with the destination node and encrypt // Calculate the shared secret with the destination node and encrypt
@@ -134,10 +194,10 @@ bool CryptoEngine::decryptCurve25519(uint32_t fromNode, meshtastic_UserLite_publ
} }
// Calculate the shared secret with the sending node and decrypt // Calculate the shared secret with the sending node and decrypt
if (!crypto->setDHPublicKey(remotePublic.bytes)) { if (!setDHPublicKey(remotePublic.bytes)) {
return false; return false;
} }
crypto->hash(shared_key, 32); hash(shared_key, 32);
initNonce(fromNode, packetNum, extraNonce); initNonce(fromNode, packetNum, extraNonce);
printBytes("Attempt decrypt with nonce: ", nonce, 13); printBytes("Attempt decrypt with nonce: ", nonce, 13);

View File

@@ -35,6 +35,8 @@ class CryptoEngine
#if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN) #if !(MESHTASTIC_EXCLUDE_PKI_KEYGEN)
virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey); virtual void generateKeyPair(uint8_t *pubKey, uint8_t *privKey);
virtual bool regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey); virtual bool regeneratePublicKey(uint8_t *pubKey, uint8_t *privKey);
bool xeddsa_sign(uint8_t *message, size_t len, uint8_t *signature);
bool xeddsa_verify(uint8_t *pubKey, uint8_t *message, size_t len, uint8_t *signature);
#endif #endif
void clearKeys(); void clearKeys();
@@ -82,6 +84,9 @@ class CryptoEngine
#if !(MESHTASTIC_EXCLUDE_PKI) #if !(MESHTASTIC_EXCLUDE_PKI)
uint8_t shared_key[32] = {0}; uint8_t shared_key[32] = {0};
uint8_t private_key[32] = {0}; uint8_t private_key[32] = {0};
uint8_t xeddsa_public_key[32] = {0};
uint8_t xeddsa_private_key[32] = {0};
void curve_to_ed_pub(uint8_t *curve_pubkey, uint8_t *ed_pubkey);
#endif #endif
/** /**
* Init our 128 bit nonce for a new packet * Init our 128 bit nonce for a new packet

View File

@@ -27,8 +27,9 @@
#ifdef USERPREFS_RINGTONE_NAG_SECS #ifdef USERPREFS_RINGTONE_NAG_SECS
#define default_ringtone_nag_secs USERPREFS_RINGTONE_NAG_SECS #define default_ringtone_nag_secs USERPREFS_RINGTONE_NAG_SECS
#else #else
#define default_ringtone_nag_secs 60 #define default_ringtone_nag_secs 15
#endif #endif
#define default_network_ipv6_enabled false
#define default_mqtt_address "mqtt.meshtastic.org" #define default_mqtt_address "mqtt.meshtastic.org"
#define default_mqtt_username "meshdev" #define default_mqtt_username "meshdev"
@@ -46,12 +47,15 @@ class Default
static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval); static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval);
static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval); static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval);
static uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue); static uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue);
// Note: numOnlineNodes uses uint32_t to match the public API and allow flexibility,
// even though internal node counts use uint16_t (max 65535 nodes)
static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes); static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes);
static uint8_t getConfiguredOrDefaultHopLimit(uint8_t configured); static uint8_t getConfiguredOrDefaultHopLimit(uint8_t configured);
static uint32_t getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue); static uint32_t getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue);
private: private:
static float congestionScalingCoefficient(int numOnlineNodes) // Note: Kept as uint32_t to match the public API parameter type
static float congestionScalingCoefficient(uint32_t numOnlineNodes)
{ {
// Increase frequency of broadcasts for small networks regardless of preset // Increase frequency of broadcasts for small networks regardless of preset
if (numOnlineNodes <= 10) { if (numOnlineNodes <= 10) {
@@ -84,4 +88,4 @@ class Default
return 1.0 + (nodesOverForty * throttlingFactor); // Each number of online node scales by 0.075 (default) return 1.0 + (nodesOverForty * throttlingFactor); // Each number of online node scales by 0.075 (default)
} }
} }
}; };

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