Compare commits

...
Author SHA1 Message Date
todd-herbertandBen Meadors 8187fa7115 E-Ink Screensaver (#3477)
* fix Wireless Paper double-clear screen at boot

* log when flooded with "responsive" frames

* show the "resuming" screen when waking from deep-sleep

* rename drawDeepSleepScreen
avoid future confusion with "Screen Paused" screen

* show a screensaver frame when screen off
The frame shown during deep sleep is now also passed through showScreensaverFrames()

* Add macros for E-Ink color values.
OLEDDISPLAY_COLOR is inverted. Result of light-mode on E-Ink vs dark-mode on OLED?

* adapt drawDeepSleepScreen to new screensaver convention

* Mark Wireless Paper V1.1 as having problems with ghosting
Any other issues can be marked in a similar way, then handled in code where relevant

* Change screensaver from fullscreen logo to overlay

* identify "quirks" rather than "problems"

* move async refresh polling from display() to a NotifiedWorkerThread

* Prevent skipping of deep-sleep screen
(Hopefully)

* Redesign screensaver overlay
Now displays short name

* Optimize refresh for different displays

* Support older EInkDisplay class

* Don't assume text alignment

* fix spelling of a quirk macro
(No impact to code, but avoids future issues)

* Handle impossibly unlikely millis() overflow error
Should have just let it go, but here we are..

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-28 18:31:11 -05:00
Ben Meadors daa4d387c6 Don't reboot for non-radio lora config changes (#3505) 2024-03-28 18:14:15 -05:00
Ben Meadors 4c2d5c6a89 Reorder structs to fix build 2024-03-28 07:16:07 -05:00
github-actions[bot]andthebentern b5ec35ec78 [create-pull-request] automated change (#3502)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-28 06:44:34 -05:00
Ben Meadors 5732eed86b Fixed position admin messages (#3490)
* Bespoke admin messages for setting and clearing fixed positions

* Add guards against remote admin messages setting things

* Flip ifs
2024-03-26 07:29:07 -05:00
Jonathan Bennett 1542afb847 Add libulfius2.7 to .deb debendencies (#3494) 2024-03-26 00:58:47 -05:00
Jim WhitelawandBen Meadors acc32916c3 Add multiple configuration options for a minimized build (GPS,WiFi,BT,MQTT,Screen). (#3469)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-25 06:33:57 -05:00
github-actions[bot]andthebentern 728b58fb94 [create-pull-request] automated change (#3489)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-24 18:50:26 -05:00
GUVWAF 77fb230baa Native: fail-safes for simulated node without config file (#3486)
* LinuxInput: only close if file descriptor is assigned

* Native: set some defaults if no configuration file found
2024-03-24 13:42:32 -05:00
Thomas Göttgens b960dc1b41 Add Shutdown and reboot to CardKB and friends (#3487)
* Add Shutdown and reboot to CardKB and friends

* aw shucks
2024-03-24 13:41:45 -05:00
code8busterandBen Meadors 5f529f7ca3 Remove unused defines from nrf52 variants (#3482)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-24 13:41:28 -05:00
github-actions[bot]andthebentern b4dbc2b4bf [create-pull-request] automated change (#3485)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-24 12:44:44 -05:00
Ben Meadors 63df972d42 Revert "[create-pull-request] automated change (#3483)" (#3484)
This reverts commit c87fdfece7.
2024-03-24 08:11:47 -05:00
github-actions[bot]andthebentern c87fdfece7 [create-pull-request] automated change (#3483)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-23 19:47:43 -05:00
GUVWAF 381d5230b8 Merge pull request #3480 from GUVWAF/logsMapReport
Don't spam logs if no position with map reporting (2)
2024-03-23 20:12:25 +01:00
GUVWAF a7c005ccdf Merge branch 'meshtastic:master' into logsMapReport 2024-03-23 19:36:21 +01:00
GUVWAF 71ca6f768f Actually update last_report_to_map 2024-03-23 19:35:12 +01:00
GUVWAF 4cce4c7c93 Set unused header bytes to zero for future use (#3479) 2024-03-23 12:38:29 -05:00
Jonathan Bennett 9e8860d188 Crash fix and remove hard-coded path from PiWebServer (#3478)
* Remove hard-coded path from PiWebServer

* Bump portduino to pick up crash fix

* Remove PiWebServer non-ASCII debug output

* Trunk formatting
2024-03-23 12:29:05 -05:00
GUVWAF d30d6bd3eb Fix #3452: only alter received packet if port number matches (#3474)
* Use `alterReceivedProtobuf()` for NeighborInfo and Traceroute
`alterReceived()` should never return NULL
Traceroute should be promiscuous

* Remove extensive logging from NeighborInfo module
2024-03-23 07:31:58 -05:00
Ben Meadors 94e4301f2f Add set and remove favorite nodes admin commands (#3471) 2024-03-22 10:53:18 -05:00
Ben Meadors 54818b5f8d Enforce consistent polite channel utilization limits except for Sensor role (#3467) 2024-03-22 07:25:00 -05:00
github-actions[bot]andthebentern c77c58d656 [create-pull-request] automated change (#3470)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-22 07:24:10 -05:00
Ben Meadors 794e99c2f9 Log warning cleanup and truth (#3466) 2024-03-21 20:45:48 -05:00
Ben Meadors 7aa013a716 Skip favorite nodes when clearing out oldest in NodeDB (#3464)
* Skip favorite nodes when clearing out oldest in NodeDB

* We should actually map between the types
2024-03-21 19:51:02 -05:00
github-actions[bot]andthebentern a57f7730ea [create-pull-request] automated change (#3463)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-21 18:55:50 -05:00
Jonathan BennettandBen Meadors 35754d661d Make MAX_NUM_NODES configurable in variant.h (#3453)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-21 18:26:37 -05:00
Jonathan Bennett 79cfb1e876 Revert "Bump actions to node 20 (#3461)" (#3462)
This reverts commit defeb8e52b.

As per https://github.com/actions/upload-artifact/issues/478 the new version of upload-artifact includes a breaking change.
2024-03-21 16:50:44 -05:00
GUVWAF 155df45d92 Add sanity check for map report interval and position precision (#3459)
* Add sanity check for map report interval and position precision

* Use new `Default::` methods
2024-03-21 16:20:20 -05:00
Jonathan Bennett 907d075917 Revert previous attempt 2024-03-21 16:17:13 -05:00
Jonathan Bennett 9c88906acc Remove double run of build-raspbian 2024-03-21 16:14:45 -05:00
Jonathan Bennett defeb8e52b Bump actions to node 20 (#3461) 2024-03-21 15:24:57 -05:00
Ben MeadorsandJonathan Bennett 6dd337a651 Clear local position on nodedb-reset (#3451)
* Clear local position on nodedb-reset

* NodeDB pointer now, yo

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2024-03-21 14:43:10 -05:00
GUVWAF 0a7ddb7594 Let NeighborInfo Module ignore packets coming from MQTT (#3457) 2024-03-21 14:42:53 -05:00
GUVWAF 4debcd5ccd Set default position precision of mapReport to 14 (#3456) 2024-03-21 14:35:17 -05:00
Jonathan Bennett fd26914d88 move nodeDB::init code into nodeDB constructor (#3455) 2024-03-21 13:14:02 -05:00
Jonathan BennettandBen Meadors dfcd0d14f6 Add MaxNodes to Native config (#3427)
* Add MaxNodes to Native

* It compiles...

* Convert nodedb to use new

* Closer but still broken.

* Finally working

* Remove unintended lines

* Don't include a pointer

* Capitalization matters.

* avoid rename in protocol regen

* When trimming the nodeDB, start with a cleanup

* Remove extra cleanupMeshDB() call for now

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-21 09:06:37 -05:00
Jim WhitelawandThomas Göttgens f4095ce00d Adds configuration option to exclude the webserver on esp32. (#3369)
* Adds configuration option to not build/include the webserver.

* Adds configuration option to not build/include the webserver.

* Keep initApiServer when excluding webserver

* fixes for failed formatting check

* Once more with feeling! Fix for regression.

* Fix includes for ARCH_ESP32

* Format changes from trunk

* Merge updates from origin

* Revert "Format changes from trunk"

This reverts commit 436e6317744576ca6b00559937ca76235b7cd66b.

* jeez!

* tryfix proto conflict

---------

Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
2024-03-21 06:34:34 -05:00
MictronicsandBen Meadors 7aa21f6e3f Fixed double and missing Default class. (#3448)
* Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28

* Merge PR #420

* Fixed double and missing Default class.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-20 10:58:48 -05:00
github-actions[bot]andthebentern 5e832e2fc6 [create-pull-request] automated change (#3444)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-19 12:02:29 -05:00
Ben Meadors 4fa7f5a748 Fix devicestate persistence bug 2024-03-19 10:31:31 -05:00
Manuel a6625998f5 fix compiler warnings in NodeDB.h (#3439)
* fix warnings on arm

* make trunk+compiler happy
2024-03-19 06:22:45 -05:00
Manuel 711b85cfe8 fix WLAN crash (#3435)
* fix WLAN crash

* link to commit in arduinothread

* revert usb mode
2024-03-18 15:42:44 -05:00
github-actions[bot]andthebentern b98176e73e [create-pull-request] automated change (#3434)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-18 07:33:01 -05:00
Ben Meadors aae49f5ecf Remove confusing channel suffix (#3432)
* Remove confusing channel suffix

* Missed it
2024-03-17 08:38:49 -05:00
Ben Meadors 0d1d79b6d1 Extract default intervals and coalesce methods into their own file / static class methods (#3425)
* Extract default intervals and coalesce methods into their own file / static class methods

* Missed pax

* Still managed to miss one
2024-03-17 08:18:30 -05:00
Ben Meadors bb57ccfc9e Remove devicestate no_save (#3424) 2024-03-17 08:16:22 -05:00
Ben Meadors e27f029d09 Bump minimum NodeInfo send to 5 minutes (#3423)
* Bump minimum NodeInfo send to 3 minutes

* 5
2024-03-16 19:56:42 -05:00
Thomas GöttgensandBen Meadors 13cc1b0252 (3/3) Add variant for pico with waveshare and GPS hat (#3412)
* (3/3) Add variant for pico with waveshare and GPS hat, utilizing slow clock.

* Not everybody has Serial2

* Trunk

* Push it real gud

* No init

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-16 10:01:43 -05:00
github-actions[bot]andthebentern 54a2a4bcc6 [create-pull-request] automated change (#3422)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-16 07:39:28 -05:00
David EllefsenandBen Meadors 611f291d4d Factory reset GNSS_MODEL_MTK GPS modules with PCAS10,3 (#3388)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-15 19:19:50 -05:00
Ben Meadors 9586606229 Handle for heartbeat toradio packets (#3420) 2024-03-15 18:40:48 -05:00
github-actions[bot]andthebentern 0de36fbfb0 [create-pull-request] automated change (#3419)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-15 17:12:45 -05:00
Andre K 0dda20bc35 fix for I2C scan getting stuck (#3375)
* refactor: add delay for T-Echo peripherals setup

* comment out `PIN_POWER_EN1`
2024-03-15 17:12:30 -05:00
Ben Meadors 52cfec29fc More comprehensive client proxy queue guards (#3414)
* More comprehensive MQTT thread and queue guards

* Consolidate logic

* Remove channel check

* Check for map_reporting_enabled as well

* Update message

* Remove channel check from here as well

* One liner

* Start the mqtt thread back up when channels change and we want mqtt
2024-03-15 16:17:47 -05:00
Thomas Göttgens 4d0d82f7e7 Merge pull request #3411 from meshtastic/rp2040-slowclock
(2/3) Add Slow Clock Support for RP2040 platform.
2024-03-15 20:56:50 +01:00
Thomas Göttgens 34bc22f94d (2/3) Add Slow Clock Support for RP2040 platform. This will disable USB Softserial. 2024-03-15 20:16:36 +01:00
Thomas Göttgens cb3740708b Merge pull request #3410 from meshtastic/gnss-l76b
(1/3) Support L76B GNSS chip found on pico waveshare shield.
2024-03-15 20:16:06 +01:00
Thomas Göttgens e8ec167854 Merge branch 'gnss-l76b' of github.com:meshtastic/firmware into gnss-l76b 2024-03-15 19:48:19 +01:00
Thomas Göttgens b900415218 that should work now 2024-03-15 19:47:47 +01:00
Thomas Göttgens 2eb78fec53 Merge branch 'master' into gnss-l76b 2024-03-15 19:39:47 +01:00
Thomas Göttgens da7cd5fc7f new Accelerometer lib (#3413)
* new Accelerometer lib

* Use our fork till upstreasm merges changes.

* that PR escalated quickly

* resurrect display flip
2024-03-15 10:45:14 -05:00
Thomas Göttgens b06c77d46f don't fix this to a hardware model. 2024-03-15 16:43:39 +01:00
Thomas Göttgens cbc0aa16c5 fix compilation 2024-03-15 16:37:47 +01:00
github-actions[bot]andthebentern 876a0520a9 [create-pull-request] automated change (#3418)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-15 08:09:48 -05:00
Thomas Göttgens 50cc4cfcf1 We don't use Lorawan (#3417)
#warning "Persistent storage not supported!" [-Wcpp]
2024-03-15 08:07:54 -05:00
Ben Meadors ec6bdeed81 NodeInfo broadcast ensure default on 0 and enforce 1 hour minimum (#3415)
* NodeInfo broadcasts ensure defaults on 0 and enforce 1 hour minumum

* Doh!

* Hey that's not on config!
2024-03-15 07:12:03 -05:00
Ben Meadors a085c3ddb3 Try-fix router missed messages (#3405) 2024-03-14 17:00:57 -05:00
Thomas Göttgens 58cdf360f8 (1/3) Support L76B GNSS chip found on pico waveshare shield. Original work by @Mictronics 2024-03-14 16:18:33 +01:00
Ben Meadors 9c37e57e75 Only allow phone to set time for fixed positions (#3403) 2024-03-13 20:27:26 -05:00
Andre KandBen Meadors 9d2fcbe1e1 use decoded packets in public MQTT range test/detection sensor filter (#3404)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-13 18:24:49 -05:00
Ben Meadors 3995e2f708 Remove bunk code 2024-03-13 15:06:52 -05:00
github-actions[bot]andthebentern 216f85ff22 [create-pull-request] automated change (#3397)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-13 09:02:48 -05:00
Ben Meadors 2efe436102 Update nrf52 platform and consolidate Adafruit Bus IO (#3393) 2024-03-13 07:20:51 -05:00
Thomas Göttgens fb16390205 Merge pull request #3395 from tavdog/patch_buzzer_no_turnoff_off_by_one_error
fix off by one error
2024-03-13 09:23:14 +01:00
Tavis 333c3c1c9e fix off by one error
buzzer is index 2, but loop was 0-1 so buzzer never got turned off.
2024-03-12 21:42:08 -10:00
GUVWAF 724fa38a55 Fix T-LoRa V2.1-6 with TCXO init (#3392) 2024-03-12 16:42:34 -05:00
Wolfgang Nagele 38ea681433 Fix LTO discharge curve (#3385)
* Fix LTO discharge curve

* Remove duplicate info
2024-03-12 16:42:21 -05:00
Wolfgang Nagele ee685b4ed7 Check AQ_SET_PIN instead of EINK dependency (#3387) 2024-03-12 13:03:04 -05:00
Thomas Herrmann cf11807f97 use priority background for low priority messages (#3381) 2024-03-12 12:21:09 -05:00
Wolfgang Nagele 7f063fbf81 Support external charge detection (#3386)
* Support external charge detection

* trunk fmt
2024-03-12 11:55:31 -05:00
Thomas Göttgens 6215495ccc Merge pull request #3379 from meshtastic/create-pull-request/patch
Changes by create-pull-request action
2024-03-12 10:26:17 +01:00
GUVWAF 045dda64e7 Merge pull request #3383 from AeroXuk/patch-1
Fix for incorrect mapTopic mqtt path
2024-03-12 08:39:30 +01:00
AeroXuk affbd7f2b9 Update MQTT.cpp
Bug fix for #3382
2024-03-12 02:13:52 +00:00
thebentern f9bf9e2dcc [create-pull-request] automated change 2024-03-11 21:43:46 +00:00
GUVWAF 5f47ca1f32 Don't spam logs if no position with map reporting (#3378) 2024-03-11 15:58:45 -05:00
Thomas Göttgens 6a27e62bcf Merge pull request #3356 from todd-herbert/eink-special-frames
Handle "special-frames" with EInkDynamicDisplay
2024-03-11 21:43:39 +01:00
Ben Meadors 2d5a6c1a20 Merge branch 'master' into eink-special-frames 2024-03-11 13:32:42 -05:00
Manuel c7839b469b fix of tryfix SHT31 sensor (#3377) 2024-03-11 12:51:14 -05:00
Ben Meadors 95967a01b8 Merge branch 'master' into eink-special-frames 2024-03-11 12:46:32 -05:00
Manuel e16689a0d6 fix heap use after delete (#3373) 2024-03-11 12:45:59 -05:00
Andre K c80098f517 refactor: remove ACKs in range tests so zero hops is honored (#3374) 2024-03-11 11:49:46 -05:00
Todd Herbert 1f766a04aa purge unused enum val 2024-03-12 04:04:28 +13:00
Todd Herbert 1d31be939f Swap Wireless Paper V1.0 dependency to meshtastic/GxEPD2 2024-03-12 03:06:01 +13:00
Thomas Göttgens 4b4bd07d5c Merge branch 'master' into eink-special-frames 2024-03-11 14:10:19 +01:00
todd-herbertandBen Meadors cf4753f7fd Async full-refresh for EInkDynamicDisplay (#3339)
* Move Wireless Paper V1.1 custom hibernate behavior to GxEPD2

* Async full-refresh for EInkDynamicDisplay

* initial config for T-Echo

* formatting
responds to https://github.com/meshtastic/firmware/pull/3339#discussion_r1518175434

* increase fast-refresh limit for T-Echo
https://github.com/meshtastic/firmware/pull/3339#issuecomment-1986245727

* change dependency from private repo to meshtastic/GxEPD2

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-11 07:56:55 -05:00
Thomas Göttgens 892223a297 fix typos and add 2 missing modules to the equasion (#3370) 2024-03-11 07:52:46 -05:00
Thomas Göttgens 658ed6fd28 tryfix SHT31 sensor on secondary bus 2024-03-11 13:51:26 +01:00
David Ellefsen 3a8f623f8a Change '! -z' to '-n' to addresss shellcheck/SC2236 2024-03-11 12:09:00 +01:00
David Ellefsen f09e5c96fc Add permission: read-all to silence CKV_GHA_1 check 2024-03-11 12:09:00 +01:00
David Ellefsen a493ab526f Trunk fmt to correct failing PR check for device-install.sh 2024-03-11 12:09:00 +01:00
David Ellefsen b3ec3c20fb Update device-install.sh files to account for bleota-c3.bin file 2024-03-11 12:09:00 +01:00
David Ellefsen b65b9e5d65 Include esp32c3 build step 2024-03-11 12:09:00 +01:00
Kevin Cai 766beefbc5 Update AccelerometerThread.h to work with T-Watch S3 2024-03-11 10:58:25 +01:00
Thomas Göttgens eb372c190e Merge pull request #3365 from GUVWAF/mapReport
Periodic reporting of device information to a map via MQTT
2024-03-10 20:55:31 +01:00
Thomas Göttgens 70df36b5db Merge branch 'master' into mapReport 2024-03-10 20:54:48 +01:00
Thomas Göttgens e33d014257 Merge pull request #3351 from thoherr/refactor-paxcounter
Refactor paxcounter
2024-03-10 18:49:16 +01:00
Thomas Herrmann 26691c0be7 include requested change and suggestions on PR from @caveman99 2024-03-10 16:08:26 +01:00
Thomas Herrmann 09e08e0091 add some documentation, cleanup 2024-03-10 16:08:26 +01:00
Thomas Herrmann 73c77b663c fix typo 2024-03-10 16:08:26 +01:00
Thomas Herrmann fb4faf790b split query of paxcounter data from sending funcionality; don't cummulate (count mode != 1); use flag to signal changed count data 2024-03-10 16:08:26 +01:00
GUVWAF cb7407e06b Don't need to check all channels if not using default frequency slot 2024-03-10 16:04:59 +01:00
GUVWAF b45a912409 Use dedicated map topic 2024-03-10 15:56:00 +01:00
Thomas Göttgens c7d5698dbc Merge branch 'master' into mapReport 2024-03-10 15:40:22 +01:00
Thomas Göttgens d1a25947e3 Merge pull request #3366 from meshtastic/create-pull-request/patch
Changes by create-pull-request action
2024-03-10 15:40:05 +01:00
caveman99 69dcc948b9 [create-pull-request] automated change 2024-03-10 14:39:40 +00:00
GUVWAF 084b01715e Merge remote-tracking branch 'origin/master' into mapReport 2024-03-10 14:54:41 +01:00
GUVWAF af9d14c370 Periodic reporting of device information to a map via MQTT 2024-03-10 14:52:37 +01:00
Todd Herbert 1032e16ea4 reorder determineMode() checks 2024-03-11 01:02:03 +13:00
Andre K 3da1b74a10 refactor: always send range tests with zero hops 2024-03-10 10:50:02 +01:00
Todd Herbert c0a3b20aa3 while drafting, build from todd-herbert/meshtastic-GxEPD2#async 2024-03-10 13:45:35 +13:00
Todd Herbert 3daae24d29 fix fallback behavior for unmodified GxEPD2
Issues exposed by https://github.com/meshtastic/firmware/pull/3356#issuecomment-1986950317
2024-03-10 13:43:57 +13:00
Jonathan Bennett dced888492 Add precision_bit sto json 2024-03-09 15:34:01 -06:00
Ben Meadors 7167f1e04f Add parens to macro (#3361) 2024-03-09 15:25:16 -06:00
Ben Meadors dfbb4cd913 Merge branch 'master' into eink-special-frames 2024-03-09 14:10:09 -06:00
GUVWAF 3da7c0dba7 Add hops_away to JSON output (#3357) 2024-03-09 11:32:49 -06:00
Todd Herbert 7b70324435 handle special frames in Screen.cpp 2024-03-10 05:00:51 +13:00
Todd Herbert 94eb837ee8 function macro for tidier addFramFlag() calls 2024-03-10 04:14:45 +13:00
Todd Herbert a9c07a4c01 add frameFlags to LOG_DEBUG() messages 2024-03-10 04:07:51 +13:00
Todd Herbert e232e3462c add BLOCKING modifier to frameFlagTypes 2024-03-10 03:48:59 +13:00
Todd Herbert 94794edd43 add init code as a determineMode() check 2024-03-10 03:43:07 +13:00
Todd Herbert 95b6f27d2a change order of determineMode() checks 2024-03-10 03:38:39 +13:00
Todd Herbert efd818fe90 move storeAndReset() to end of update() 2024-03-10 03:07:13 +13:00
Todd Herbert 576f582cd9 rename setFrameFlag() method 2024-03-10 02:30:16 +13:00
Todd Herbert d5c11d1892 change dependency from private repo to meshtastic/GxEPD2 2024-03-10 02:11:49 +13:00
Ben Meadors aaa5d61162 Merge branch 'master' into eink-async 2024-03-09 07:06:04 -06:00
Ben Meadors 3efd606ea7 Bump to 2.3.0 2024-03-09 07:01:46 -06:00
Ben Meadors 42286edc81 Merge branch 'master' into eink-async 2024-03-09 06:59:56 -06:00
Mark Trevor Birss 29335a18f5 Update variant.h (#3354) 2024-03-09 06:55:02 -06:00
Andre KandBen Meadors 51df4fc775 fix: turn off T-Echo peripherals on deep sleep (#3162)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-08 20:15:37 -06:00
Ben Meadors 0f1bc98305 Update MQTT topic to match (#3353) 2024-03-08 20:15:00 -06:00
Todd Herbert 23926210d1 increase fast-refresh limit for T-Echo
https://github.com/meshtastic/firmware/pull/3339#issuecomment-1986245727
2024-03-09 09:57:30 +13:00
Todd Herbert 7275c21f6b formatting
responds to https://github.com/meshtastic/firmware/pull/3339#discussion_r1518175434
2024-03-09 09:34:53 +13:00
Todd Herbert ac89bb3387 initial config for T-Echo 2024-03-09 09:30:34 +13:00
Todd Herbert 07da130586 Async full-refresh for EInkDynamicDisplay 2024-03-09 09:30:34 +13:00
Todd Herbert 5d4d91f775 Move Wireless Paper V1.1 custom hibernate behavior to GxEPD2 2024-03-09 09:30:34 +13:00
Ben Meadors 7da1153c2c Fix known_only panic by short circuiting for NULL before checking has_user (#3352) 2024-03-08 08:31:49 -06:00
GUVWAFandBen Meadors 585805c3b9 Add original hop limit to header to determine hops used (#3321)
* Set `hop_start` in header to determine how many hops each packet traveled

* Set hopLimit of response according to hops used by request

* Identify neighbors based on `hopStart` and `hopLimit`

* NeighborInfo: get all packets and assume a default broadcast interval

* Add fail-safe in case node in between is running modified firmware

* Add `viaMQTT` and `hopsAway` to NodeInfo

* Replace `HOP_RELIABLE` with hopStart for repeated packet

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-08 07:13:57 -06:00
Thomas Göttgens a4830e0ab1 Merge pull request #3347 from thoherr/add-bmp085_180-sensor
add BMP085 (and BMP180) sensor (temperature and air pressure)
2024-03-08 12:02:43 +01:00
Thomas Herrmann 763ae9f2e2 add BMP085 (and BMP180) sensor (temperature and air pressure) 2024-03-07 23:58:04 +01:00
Ben Meadors 7f12505716 Update trunk 2024-03-07 15:52:08 -06:00
Ben Meadors b4940b476d Trunk 2024-03-07 15:51:28 -06:00
Steven Osborn c860493e68 Add delay so GPS and Radio have time to power up (#3334)
* Add delay so GPS and Radio have time to power up

* reduce the delay a bit

* make delay more generic / configurable

* remove whitespace changes
2024-03-07 07:11:25 -06:00
github-actions[bot]andthebentern 2dd751e339 [create-pull-request] automated change (#3346)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-03-07 07:06:47 -06:00
Jonathan Bennett bfce3938d2 Add openssl as dependency to meshtasticd .deb 2024-03-06 18:54:09 -06:00
Jonathan Bennett 46ad623785 Add webroot to .deb 2024-03-06 17:43:04 -06:00
e174328de3 Native Webserver (#3343)
* Added WebServer/WebServices for Native Linux Meshtastic and web gui

* Fix bug in login functionality

* Added customized config of portdunio.ini with LovyannGFX from marelab repro

* Compile Problem resolved with developer version of LovyanGFX.git

* Compile against dev version

* Fixes to fit into main branch

* Update variant.h, main.cpp, .gitignore, WebServer.cpp, esp32s2.ini, WebServer.h, ContentHandler.cpp, rp2040.ini, nrf52.ini, ContentHelper.cpp, Dockerfile, ContentHandler.h, esp32.ini, stm32wl5e.ini

* Added linux pi std /usr/include dir

* Adding /usr/innclude for Linux compile against native libs that are not hadled by platformio

* Review log level changes & translation

* Update Dockerfile

* Fix Typo & VFS ref. Part1

* Fix Typo & VFS ref.

* Dev Version for ulfius web lib

* Update platformio.ini

* Free VFS path string

* Remove unintended changes

* More unintentional changes

* Make the HTTP server optional on native

* Tune-up for Native web defaults

* Don't modify build system yet

* Remove more unneeded changes

---------

Co-authored-by: marc hammermann <marchammermann@googlemail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
2024-03-06 16:23:04 -06:00
Jonathan Bennett 9d37a8d17f Stop Fiddling with Newlines! (#3341) 2024-03-06 13:09:46 -06:00
Thomas Göttgens f5ff77c2b9 Turn off certain modules not wanted in custom builds (#3337) 2024-03-05 07:50:52 -06:00
Ben Meadors 72050530f1 NRF52 bluetooth cleanup and fix (#3328)
* NRF52 bluetooth cleanup. Fixes BLE not returning after serial PhoneAPI connection

* Use new var name in esp32 arch
2024-03-03 13:56:55 -06:00
e5bf07d4fb Fix for issue #3310 (#3327)
* Portduino multiple logging levels

* Fixes based on GPSFan work

* Fix derped logic

* Correct size field for AID message

* Reformat to add comments, beginning of GPS rework

* Update PM2 message for Neo-6

* Correct ECO mode logic as ECO mode is only for Neo-6

* Cleanup ubx.h add a few more comments

* GPS rework, changes for M8 and stub for M10

* Add VALSET commands for u-blox M10 receivers

* Add VALSET commands for u-blox M10 receivers
tweak M8 commands
add comments for VALSET configuration commands

* Add commands to init M10 receivers,
tweak the M8 init sequence, this is a WIP as there are still some issues during init.
Add M10 version of PMREQ.

* Add wakeup source of uartrx to PMREQ_10
The M10 does not respond to commands when asleep,
may need to do this for the M8 as well

* Enable NMEA messages on USB port.
Normally, it is a good idea to disable messages on unused ports.
Native Linux needs to be able to use GNSS modules connected via
via either serial or USB.
In the future I2C connections may be required, but are not enabled for now.

* Save the config for all u-blox receiver types.
The M10 supports this command in addition to saving using
the VALSET commands for the RAM & BBR layers.

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-03 13:08:47 -06:00
Jonathan Bennett 7ab9a94edb just off the coast of NULL Island isn't OK either. 2024-03-03 11:50:07 -06:00
Ben Meadors 3c3d391044 Remove rangetest file on factory reset (#3322) 2024-03-03 10:33:30 -06:00
Ben Meadors e3063a2785 Turns out bluefruit uses some of these macros even though "we" don't :-/ 2024-03-03 09:46:36 -06:00
Ben Meadors 6dbb6583ef Put these back 2024-03-03 09:33:18 -06:00
Ben Meadors 9b3e519487 Revert "Fix LED pinout for T-Echo board marked v1.0" (#3304)
* Revert "Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28 (#3051)"

This reverts commit c2afa879b8.

* Remove / comment out unused LED pins
2024-03-03 08:55:52 -06:00
Ben Meadors 495840c777 Filter out neighborinfo if we don't have the module enabled (#3314)
* Filter out neighborinfo if we don't have the module enabled

* Handlereceived instead

* Add debug message
2024-03-03 08:36:36 -06:00
todd-herbert 5865add857 E-Ink: fast refresh for Wireless Paper V1.1 (#3320) 2024-03-03 07:13:56 -06:00
todd-herbertandBen Meadors c659292836 Reimplement "Dynamic E-Ink" as a derived class (#3316)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-03-02 20:07:29 -06:00
Ben Meadors 905718e2ac Remove problematic IO2 3V3 toggling (again) as GPS EN pin (#3317)
* Remove problematic IO2 3V3 toggling (again) as GPS EN pin

* Comment

* Trunk
2024-03-02 15:45:59 -06:00
Ben Meadors a58348369d Update protos and add new fields to type conversions (#3315) 2024-03-02 10:20:27 -06:00
Ben Meadors d20fa6e927 Comment out ReplyModule registration, but leave it in as example (#3312) 2024-03-02 07:21:53 -06:00
Ben Meadors bf88773b6b Don't send anybody to null island ever (#3308) 2024-02-28 13:44:15 -06:00
todd-herbertandBen Meadors 6acc63729b Refactor EInkDisplay (#3299)
* Refactor EInkDisplay
A lot of variant specific code is merged, with the macros pushed to the respective variant.h files.
"Dynamic Partial" code has been purged, pending a rewrite.

* fix: declare class only if USE_EINK, init all members

* refactor: move macros to platformio.ini
Responds to https://github.com/meshtastic/firmware/pull/3299#issuecomment-1966425926

* fix: EInkDisplay::connect() references old macros
Usage was in a block of variant-specific code, which had been intentionally left untouched.

* fix: remove duplicate macros from variant.h

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-02-28 09:45:15 -06:00
todd-herbert 7aee014f5e Add Heltec Wireless Paper V1.0 to the build matrix (#3306) 2024-02-28 09:20:20 -06:00
Ben Meadors 2786db499d Missing include 2024-02-28 08:01:59 -06:00
Thomas Herrmann 4ffb906fe8 move duplicate #define for screen to unified header file (#3302) 2024-02-27 12:49:46 -06:00
github-actions[bot]andthebentern f7758b4e44 [create-pull-request] automated change (#3298)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2024-02-26 21:32:49 -06:00
195 changed files with 3850 additions and 1921 deletions
+1
View File
@@ -35,6 +35,7 @@ jobs:
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32.ini
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s2.ini
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s3.ini
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32c3.ini
- name: Build ESP32
run: bin/build-esp32.sh ${{ inputs.board }}
+62
View File
@@ -0,0 +1,62 @@
name: Build ESP32-C3
on:
workflow_call:
inputs:
board:
required: true
type: string
permissions: read-all
jobs:
build-esp32-c3:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build base
id: base
uses: ./.github/actions/setup-base
- name: Pull web ui
uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4
with:
repo: meshtastic/web
file: build.tar
target: build.tar
token: ${{ secrets.GITHUB_TOKEN }}
- name: Unpack web ui
run: |
tar -xf build.tar -C data/static
rm build.tar
- name: Remove debug flags for release
if: ${{ github.event_name == 'workflow_dispatch' }}
run: |
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32.ini
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s2.ini
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s3.ini
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32c3.ini
- name: Build ESP32
run: bin/build-esp32.sh ${{ inputs.board }}
- name: Pull OTA Firmware
uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4
with:
repo: meshtastic/firmware-ota
file: firmware-c3.bin
target: release/bleota-c3.bin
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get release version string
shell: bash
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Store binaries as an artifact
uses: actions/upload-artifact@v3
with:
name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
path: |
release/*.bin
release/*.elf
+1
View File
@@ -34,6 +34,7 @@ jobs:
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32.ini
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s2.ini
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32s3.ini
sed -i '/DDEBUG_HEAP/d' ./arch/esp32/esp32c3.ini
- name: Build ESP32
run: bin/build-esp32.sh ${{ inputs.board }}
+14 -3
View File
@@ -67,7 +67,6 @@ jobs:
- board: tlora-v2-1-1_6-tcxo
- board: tlora-v2-1-1_8
- board: tbeam
- board: heltec-ht62-esp32c3-sx1262
- board: heltec-v2_0
- board: heltec-v2_1
- board: tbeam0_7
@@ -93,7 +92,8 @@ jobs:
- board: heltec-wsl-v3
- board: heltec-wireless-tracker
- board: heltec-wireless-tracker-V1-0
- board: heltec-wireless-paper
- board: heltec-wireless-paper-v1_0
- board: heltec-wireless-paper #v1.1
- board: tbeam-s3-core
- board: tlora-t3s3-v1
- board: t-watch-s3
@@ -104,6 +104,16 @@ jobs:
with:
board: ${{ matrix.board }}
build-esp32-c3:
strategy:
fail-fast: false
matrix:
include:
- board: heltec-ht62-esp32c3-sx1262
uses: ./.github/workflows/build_esp32_c3.yml
with:
board: ${{ matrix.board }}
build-nrf52:
strategy:
fail-fast: false
@@ -225,6 +235,7 @@ jobs:
[
build-esp32,
build-esp32-s3,
build-esp32-c3,
build-nrf52,
build-raspbian,
build-native,
@@ -250,7 +261,7 @@ jobs:
id: version
- name: Move files up
run: mv -b -t ./ ./*tbeam-2*/littlefs*.bin ./*tbeam-2*/bleota.bin ./*tbeam-s3*/bleota-s3.bin ./**/firmware*.bin ./*t-echo*/Meshtastic_nRF52_factory_erase_v2.uf2 ./**/firmware-*.uf2 ./**/firmware-*-ota.zip ./**/*.elf ./*native*/*device-*.sh ./*native*/*device-*.bat ./firmware-raspbian-*/release/meshtasticd_linux_aarch64 ./firmware-raspbian-*/bin/config-dist.yaml
run: mv -b -t ./ ./*tbeam-2*/littlefs*.bin ./*tbeam-2*/bleota.bin ./*tbeam-s3*/bleota-s3.bin ./*esp32c3*/bleota-c3.bin ./**/firmware*.bin ./*t-echo*/Meshtastic_nRF52_factory_erase_v2.uf2 ./**/firmware-*.uf2 ./**/firmware-*-ota.zip ./**/*.elf ./*native*/*device-*.sh ./*native*/*device-*.bat ./firmware-raspbian-*/release/meshtasticd_linux_aarch64 ./firmware-raspbian-*/bin/config-dist.yaml
- name: Repackage in single firmware zip
uses: actions/upload-artifact@v3
+12 -1
View File
@@ -23,6 +23,14 @@ jobs:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Pull web ui
uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4
with:
repo: meshtastic/web
file: build.tar
target: build.tar
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
@@ -37,9 +45,12 @@ jobs:
- name: build .debpkg
run: |
mkdir -p .debpkg/usr/share/doc/meshtasticd/web
mkdir -p .debpkg/usr/sbin
mkdir -p .debpkg/etc/meshtasticd
mkdir -p .debpkg/usr/lib/systemd/system/
tar -xf build.tar -C .debpkg/usr/share/doc/meshtasticd/web
gunzip .debpkg/usr/share/doc/meshtasticd/web/*.gz
cp release/meshtasticd_linux_aarch64 .debpkg/usr/sbin/meshtasticd
cp bin/config-dist.yaml .debpkg/etc/meshtasticd/config.yaml
chmod +x .debpkg/usr/sbin/meshtasticd
@@ -52,7 +63,7 @@ jobs:
maintainer: Jonathan Bennett
version: ${{ steps.version.outputs.version }} # refs/tags/v*.*.*
arch: arm64
depends: libyaml-cpp0.7
depends: libyaml-cpp0.7, openssl, libulfius2.7
desc: Native Linux Meshtastic binary.
- uses: actions/upload-artifact@v3
+2
View File
@@ -31,3 +31,5 @@ venv/
release/
.vscode/extensions.json
/compile_commands.json
src/mesh/raspihttp/certificate.pem
src/mesh/raspihttp/private_key.pem
+5 -5
View File
@@ -4,19 +4,19 @@ cli:
plugins:
sources:
- id: trunk
ref: v1.4.3
ref: v1.4.4
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- trufflehog@3.68.2
- trufflehog@3.68.5
- yamllint@1.35.1
- bandit@1.7.7
- checkov@3.2.26
- terrascan@1.18.11
- checkov@3.2.32
- terrascan@1.19.1
- trivy@0.49.1
#- trufflehog@3.63.2-rc0
- taplo@0.8.1
- ruff@0.2.2
- ruff@0.3.1
- isort@5.13.2
- markdownlint@0.39.0
- oxipng@9.0.0
+1 -1
View File
@@ -6,4 +6,4 @@
"platformio.platformio-ide",
"trunk.io"
],
}
}
+3 -1
View File
@@ -1,5 +1,7 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "trunk.io",
"trunk.enableWindows": true
"trunk.enableWindows": true,
"files.insertFinalNewline": false,
"files.trimFinalNewlines": false
}
+1 -1
View File
@@ -4,7 +4,7 @@ extends = arduino_base
platform = platformio/espressif32@6.3.2 # This is a temporary fix to the S3-based devices bluetooth issues until we can determine what within ESP-IDF changed and can develop a suitable patch.
build_src_filter =
${arduino_base.build_src_filter} -<platform/nrf52/> -<platform/stm32wl> -<platform/rp2040> -<mesh/eth/>
${arduino_base.build_src_filter} -<platform/nrf52/> -<platform/stm32wl> -<platform/rp2040> -<mesh/eth/> -<mesh/raspihttp>
upload_speed = 921600
debug_init_break = tbreak setup
+2 -3
View File
@@ -2,7 +2,7 @@
extends = esp32_base
build_src_filter =
${esp32_base.build_src_filter} -<nimble/>
${esp32_base.build_src_filter} -<nimble/> -<mesh/raspihttp>
monitor_speed = 115200
@@ -12,5 +12,4 @@ build_flags =
lib_ignore =
${esp32_base.lib_ignore}
NimBLE-Arduino
NimBLE-Arduino
+2 -2
View File
@@ -1,6 +1,6 @@
[nrf52_base]
; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files
platform = platformio/nordicnrf52@^10.1.0
platform = platformio/nordicnrf52@^10.4.0
extends = arduino_base
build_type = debug ; I'm debugging with ICE a lot now
@@ -11,7 +11,7 @@ build_flags =
-Isrc/platform/nrf52
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2040> -<mesh/eth/>
${arduino_base.build_src_filter} -<platform/esp32/> -<platform/stm32wl> -<nimble/> -<mesh/wifi/> -<mesh/api/> -<mesh/http/> -<modules/esp32> -<platform/rp2040> -<mesh/eth/> -<mesh/raspihttp>
lib_deps=
${arduino_base.lib_deps}
+2 -1
View File
@@ -1,6 +1,6 @@
; The Portduino based sim environment on top of any host OS, all hardware will be simulated
[portduino_base]
platform = https://github.com/meshtastic/platform-native.git#a28dd5a9ccd5c48a9bede46037855ff83915d74b
platform = https://github.com/meshtastic/platform-native.git#1b8a32c60ab7495026033858d53c737f7d1cb34a
framework = arduino
build_src_filter =
@@ -12,6 +12,7 @@ build_src_filter =
-<platform/rp2040>
-<mesh/wifi/>
-<mesh/http/>
+<mesh/raspihttp/>
-<mesh/eth/>
-<modules/esp32>
-<modules/Telemetry/EnvironmentTelemetry.cpp>
+3 -3
View File
@@ -1,8 +1,8 @@
; Common settings for rp2040 Processor based targets
[rp2040_base]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#612de5399d68b359053f1307ed223d400aea975c
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#60d6ae81fcc73c34b1493ca9e261695e471bc0c2
extends = arduino_base
platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.6.2
platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#3.7.2
board_build.core = earlephilhower
board_build.filesystem_size = 0.5m
@@ -12,7 +12,7 @@ build_flags =
-D__PLAT_RP2040__
# -D _POSIX_THREADS
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<modules/esp32> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/> -<mesh/wifi/> -<mesh/http/>
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<modules/esp32> -<platform/nrf52/> -<platform/stm32wl> -<mesh/eth/> -<mesh/wifi/> -<mesh/http/> -<mesh/raspihttp>
lib_ignore =
BluetoothOTA
+1 -1
View File
@@ -13,7 +13,7 @@ build_flags =
-DVECT_TAB_OFFSET=0x08000000
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/Telemetry> -<platform/nrf52> -<platform/portduino> -<platform/rp2040>
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/Telemetry> -<platform/nrf52> -<platform/portduino> -<platform/rp2040> -<mesh/raspihttp>
board_upload.offset_address = 0x08000000
upload_protocol = stlink
+7
View File
@@ -117,3 +117,10 @@ Input:
Logging:
LogLevel: info # debug, info, warn, error
Webserver:
# Port: 443 # Port for Webserver & Webservices
# RootPath: /usr/share/doc/meshtasticd/web # Root Dir of WebServer
General:
MaxNodes: 200
+6 -2
View File
@@ -31,9 +31,13 @@ IF EXIST %FILENAME% IF x%FILENAME:update=%==x%FILENAME% (
%PYTHON% -m esptool --baud 115200 erase_flash
%PYTHON% -m esptool --baud 115200 write_flash 0x00 %FILENAME%
@REM Account for S3 board's different OTA partition
@REM Account for S3 and C3 board's different OTA partition
IF x%FILENAME:s3=%==x%FILENAME% IF x%FILENAME:v3=%==x%FILENAME% IF x%FILENAME:t-deck=%==x%FILENAME% IF x%FILENAME:wireless-paper=%==x%FILENAME% IF x%FILENAME:wireless-tracker=%==x%FILENAME% (
%PYTHON% -m esptool --baud 115200 write_flash 0x260000 bleota.bin
IF x%FILENAME:esp32c3=%==x%FILENAME% (
%PYTHON% -m esptool --baud 115200 write_flash 0x260000 bleota.bin
) else (
%PYTHON% -m esptool --baud 115200 write_flash 0x260000 bleota-c3.bin
)
) else (
%PYTHON% -m esptool --baud 115200 write_flash 0x260000 bleota-s3.bin
)
+36 -30
View File
@@ -1,12 +1,12 @@
#!/bin/sh
PYTHON=${PYTHON:-$(which python3 python|head -n 1)}
PYTHON=${PYTHON:-$(which python3 python | head -n 1)}
set -e
# Usage info
show_help() {
cat << EOF
cat <<EOF
Usage: $(basename $0) [-h] [-p ESPTOOL_PORT] [-P PYTHON] [-f FILENAME|FILENAME]
Flash image file to device, but first erasing and writing system information"
@@ -18,44 +18,50 @@ Flash image file to device, but first erasing and writing system information"
EOF
}
while getopts ":hp:P:f:" opt; do
case "${opt}" in
h)
show_help
exit 0
;;
p) export ESPTOOL_PORT=${OPTARG}
;;
P) PYTHON=${OPTARG}
;;
f) FILENAME=${OPTARG}
;;
*)
echo "Invalid flag."
show_help >&2
exit 1
;;
esac
case "${opt}" in
h)
show_help
exit 0
;;
p)
export ESPTOOL_PORT=${OPTARG}
;;
P)
PYTHON=${OPTARG}
;;
f)
FILENAME=${OPTARG}
;;
*)
echo "Invalid flag."
show_help >&2
exit 1
;;
esac
done
shift "$((OPTIND-1))"
shift "$((OPTIND - 1))"
[ -z "$FILENAME" -a -n "$1" ] && {
FILENAME=$1
shift
FILENAME=$1
shift
}
if [ -f "${FILENAME}" ] && [ ! -z "${FILENAME##*"update"*}" ]; then
if [ -f "${FILENAME}" ] && [ -n "${FILENAME##*"update"*}" ]; then
echo "Trying to flash ${FILENAME}, but first erasing and writing system information"
"$PYTHON" -m esptool erase_flash
"$PYTHON" -m esptool write_flash 0x00 ${FILENAME}
"$PYTHON" -m esptool erase_flash
"$PYTHON" -m esptool write_flash 0x00 ${FILENAME}
# Account for S3 board's different OTA partition
if [ ! -z "${FILENAME##*"s3"*}" ] && [ ! -z "${FILENAME##*"-v3"*}" ] && [ ! -z "${FILENAME##*"t-deck"*}" ] && [ ! -z "${FILENAME##*"wireless-paper"*}" ] && [ ! -z "${FILENAME##*"wireless-tracker"*}" ]; then
"$PYTHON" -m esptool write_flash 0x260000 bleota.bin
if [ -n "${FILENAME##*"s3"*}" ] && [ -n "${FILENAME##*"-v3"*}" ] && [ -n "${FILENAME##*"t-deck"*}" ] && [ -n "${FILENAME##*"wireless-paper"*}" ] && [ -n "${FILENAME##*"wireless-tracker"*}" ]; then
if [ -n "${FILENAME##*"esp32c3"*}" ]; then
"$PYTHON" -m esptool write_flash 0x260000 bleota.bin
else
"$PYTHON" -m esptool write_flash 0x260000 bleota-c3.bin
fi
else
"$PYTHON" -m esptool write_flash 0x260000 bleota-s3.bin
"$PYTHON" -m esptool write_flash 0x260000 bleota-s3.bin
fi
"$PYTHON" -m esptool write_flash 0x300000 littlefs-*.bin
"$PYTHON" -m esptool write_flash 0x300000 littlefs-*.bin
else
show_help
+1 -1
View File
@@ -1 +1 @@
cd protobufs && ..\nanopb-0.4.7\generator-bin\protoc.exe --experimental_allow_proto3_optional --nanopb_out=-v:..\src\mesh\generated -I=..\protobufs ..\protobufs\meshtastic\*.proto
cd protobufs && ..\nanopb-0.4.7\generator-bin\protoc.exe --experimental_allow_proto3_optional "--nanopb_out=-S.cpp -v:..\src\mesh\generated\" -I=..\protobufs ..\protobufs\meshtastic\*.proto
+1 -2
View File
@@ -8,9 +8,8 @@ echo "prebuilt binaries for your computer into nanopb-0.4.7"
# the nanopb tool seems to require that the .options file be in the current directory!
cd protobufs
../nanopb-0.4.7/generator-bin/protoc --nanopb_out=-v:../src/mesh/generated/ -I=../protobufs meshtastic/*.proto --experimental_allow_proto3_optional
../nanopb-0.4.7/generator-bin/protoc "--nanopb_out=-S.cpp -v:../src/mesh/generated/" -I=../protobufs meshtastic/*.proto --experimental_allow_proto3_optional
# cd ../src/mesh/generated/meshtastic
# sed -i 's/#include "meshtastic/#include "./g' -- *
# sed -i 's/meshtastic_//g' -- *
+4 -1
View File
@@ -7,7 +7,10 @@
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_CANARY -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [["0x239A", "0x4405"]],
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x009F"]
],
"usb_product": "CanaryOne",
"mcu": "nrf52840",
"variant": "canaryone",
+6 -4
View File
@@ -69,6 +69,7 @@ build_flags = -Wno-missing-field-initializers
-DRADIOLIB_EXCLUDE_PAGER
-DRADIOLIB_EXCLUDE_FSK4
-DRADIOLIB_EXCLUDE_APRS
-DRADIOLIB_EXCLUDE_LORAWAN
monitor_speed = 115200
@@ -77,8 +78,8 @@ lib_deps =
https://github.com/meshtastic/esp8266-oled-ssd1306.git#ee628ee6c9588d4c56c9e3da35f0fc9448ad54a8 ; ESP8266_SSD1306
mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
https://github.com/meshtastic/TinyGPSPlus.git#2044b2c51e91ab4cd8cc93b15e40658cd808dd06
https://github.com/meshtastic/ArduinoThread.git#72921ac222eed6f526ba1682023cee290d9aa1b3
https://github.com/meshtastic/TinyGPSPlus.git#f9f4fef2183514aa52be91d714c1455dd6f26e45
https://github.com/meshtastic/ArduinoThread.git#1ae8778c85d0a2a729f989e0b1e7d7c4dc84eef0
nanopb/Nanopb@^0.4.7
erriez/ErriezCRC32@^1.0.1
@@ -113,9 +114,10 @@ lib_deps =
; (not included in native / portduino)
[environmental_base]
lib_deps =
adafruit/Adafruit BusIO@^1.11.4
adafruit/Adafruit BusIO@^1.15.0
adafruit/Adafruit Unified Sensor@^1.1.11
adafruit/Adafruit BMP280 Library@^2.6.8
adafruit/Adafruit BMP085 Library@^1.2.4
adafruit/Adafruit BME280 Library@^2.2.2
https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.5.2400
boschsensortec/BME68x Sensor Library@^1.1.40407
@@ -129,4 +131,4 @@ lib_deps =
adafruit/Adafruit PM25 AQI Sensor@^1.0.6
adafruit/Adafruit MPU6050@^2.2.4
adafruit/Adafruit LIS3DH@^1.2.4
https://github.com/lewisxhe/BMA423_Library@^0.0.1
https://github.com/lewisxhe/SensorLib#27fd0f721e20cd09e1f81383f0ba58a54fe84a17
+26 -39
View File
@@ -7,16 +7,16 @@
#include <Adafruit_LIS3DH.h>
#include <Adafruit_MPU6050.h>
#include <Arduino.h>
#include <SensorBMA423.hpp>
#include <Wire.h>
#include <bma.h>
BMA423 bmaSensor;
SensorBMA423 bmaSensor;
bool BMA_IRQ = false;
#define ACCELEROMETER_CHECK_INTERVAL_MS 100
#define ACCELEROMETER_CLICK_THRESHOLD 40
uint16_t readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint16_t len)
int readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len)
{
Wire.beginTransmission(address);
Wire.write(reg);
@@ -29,7 +29,7 @@ uint16_t readRegister(uint8_t address, uint8_t reg, uint8_t *data, uint16_t len)
return 0; // Pass
}
uint16_t writeRegister(uint8_t address, uint8_t reg, uint8_t *data, uint16_t len)
int writeRegister(uint8_t address, uint8_t reg, uint8_t *data, uint8_t len)
{
Wire.beginTransmission(address);
Wire.write(reg);
@@ -72,24 +72,14 @@ class AccelerometerThread : public concurrency::OSThread
lis.setRange(LIS3DH_RANGE_2_G);
// Adjust threshold, higher numbers are less sensitive
lis.setClick(config.device.double_tap_as_button_press ? 2 : 1, ACCELEROMETER_CLICK_THRESHOLD);
} else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 && bmaSensor.begin(readRegister, writeRegister, delay)) {
} else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 &&
bmaSensor.begin(accelerometer_found.address, &readRegister, &writeRegister)) {
LOG_DEBUG("BMA423 initializing\n");
Acfg cfg;
cfg.odr = BMA4_OUTPUT_DATA_RATE_100HZ;
cfg.range = BMA4_ACCEL_RANGE_2G;
cfg.bandwidth = BMA4_ACCEL_NORMAL_AVG4;
cfg.perf_mode = BMA4_CONTINUOUS_MODE;
bmaSensor.setAccelConfig(cfg);
bmaSensor.enableAccel();
struct bma4_int_pin_config pin_config;
pin_config.edge_ctrl = BMA4_LEVEL_TRIGGER;
pin_config.lvl = BMA4_ACTIVE_HIGH;
pin_config.od = BMA4_PUSH_PULL;
pin_config.output_en = BMA4_OUTPUT_ENABLE;
pin_config.input_en = BMA4_INPUT_DISABLE;
// The correct trigger interrupt needs to be configured as needed
bmaSensor.setINTPinConfig(pin_config, BMA4_INTR1_MAP);
bmaSensor.configAccelerometer(bmaSensor.RANGE_2G, bmaSensor.ODR_100HZ, bmaSensor.BW_NORMAL_AVG4,
bmaSensor.PERF_CONTINUOUS_MODE);
bmaSensor.enableAccelerometer();
bmaSensor.configInterrupt(BMA4_LEVEL_TRIGGER, BMA4_ACTIVE_HIGH, BMA4_PUSH_PULL, BMA4_OUTPUT_ENABLE,
BMA4_INPUT_DISABLE);
#ifdef BMA423_INT
pinMode(BMA4XX_INT, INPUT);
@@ -102,25 +92,22 @@ class AccelerometerThread : public concurrency::OSThread
RISING); // Select the interrupt mode according to the actual circuit
#endif
struct bma423_axes_remap remap_data;
remap_data.x_axis = 0;
remap_data.x_axis_sign = 1;
remap_data.y_axis = 1;
remap_data.y_axis_sign = 0;
remap_data.z_axis = 2;
remap_data.z_axis_sign = 1;
#ifdef T_WATCH_S3
// Need to raise the wrist function, need to set the correct axis
bmaSensor.setRemapAxes(&remap_data);
// sensor.enableFeature(BMA423_STEP_CNTR, true);
bmaSensor.enableFeature(BMA423_TILT, true);
bmaSensor.enableFeature(BMA423_WAKEUP, true);
// sensor.resetStepCounter();
bmaSensor.setReampAxes(bmaSensor.REMAP_TOP_LAYER_RIGHT_CORNER);
#else
bmaSensor.setReampAxes(bmaSensor.REMAP_BOTTOM_LAYER_BOTTOM_LEFT_CORNER);
#endif
// bmaSensor.enableFeature(bmaSensor.FEATURE_STEP_CNTR, true);
bmaSensor.enableFeature(bmaSensor.FEATURE_TILT, true);
bmaSensor.enableFeature(bmaSensor.FEATURE_WAKEUP, true);
// bmaSensor.resetPedometer();
// Turn on feature interrupt
bmaSensor.enableStepCountInterrupt();
bmaSensor.enableTiltInterrupt();
bmaSensor.enablePedometerIRQ();
bmaSensor.enableTiltIRQ();
// It corresponds to isDoubleClick interrupt
bmaSensor.enableWakeupInterrupt();
bmaSensor.enableWakeupIRQ();
}
}
@@ -141,8 +128,8 @@ class AccelerometerThread : public concurrency::OSThread
buttonPress();
return 500;
}
} else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 && bmaSensor.getINT()) {
if (bmaSensor.isTilt() || bmaSensor.isDoubleClick()) {
} else if (acceleremoter_type == ScanI2C::DeviceType::BMA423 && bmaSensor.readIrqStatus() != DEV_WIRE_NONE) {
if (bmaSensor.isTilt() || bmaSensor.isDoubleTap()) {
wakeScreen();
return 500;
}
@@ -171,4 +158,4 @@ class AccelerometerThread : public concurrency::OSThread
Adafruit_LIS3DH lis;
};
} // namespace concurrency
} // namespace concurrency
+5 -3
View File
@@ -1,10 +1,12 @@
#include "ButtonThread.h"
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h"
#endif
#include "MeshService.h"
#include "PowerFSM.h"
#include "RadioLibInterface.h"
#include "buzz.h"
#include "graphics/Screen.h"
#include "main.h"
#include "modules/ExternalNotificationModule.h"
#include "power.h"
@@ -145,7 +147,7 @@ int32_t ButtonThread::runOnce()
screen->print("Sent ad-hoc ping\n");
break;
}
#if HAS_GPS
case BUTTON_EVENT_MULTI_PRESSED: {
LOG_BUTTON("Multi press!\n");
if (!config.device.disable_triple_click && (gps != nullptr)) {
@@ -155,7 +157,7 @@ int32_t ButtonThread::runOnce()
}
break;
}
#endif
case BUTTON_EVENT_LONG_PRESSED: {
LOG_BUTTON("Long press!\n");
powerFSM.trigger(EVENT_PRESS);
+7 -18
View File
@@ -4,8 +4,6 @@
#include "configuration.h"
#include <Arduino.h>
extern NodeDB nodeDB;
namespace meshtastic
{
@@ -55,7 +53,7 @@ class GPSStatus : public Status
#ifdef GPS_EXTRAVERBOSE
LOG_WARN("Using fixed latitude\n");
#endif
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
return node->position.latitude_i;
} else {
return p.latitude_i;
@@ -68,7 +66,7 @@ class GPSStatus : public Status
#ifdef GPS_EXTRAVERBOSE
LOG_WARN("Using fixed longitude\n");
#endif
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
return node->position.longitude_i;
} else {
return p.longitude_i;
@@ -81,27 +79,18 @@ class GPSStatus : public Status
#ifdef GPS_EXTRAVERBOSE
LOG_WARN("Using fixed altitude\n");
#endif
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
return node->position.altitude;
} else {
return p.altitude;
}
}
uint32_t getDOP() const
{
return p.PDOP;
}
uint32_t getDOP() const { return p.PDOP; }
uint32_t getHeading() const
{
return p.ground_track;
}
uint32_t getHeading() const { return p.ground_track; }
uint32_t getNumSatellites() const
{
return p.sats_in_view;
}
uint32_t getNumSatellites() const { return p.sats_in_view; }
bool matches(const GPSStatus *newStatus) const
{
@@ -149,4 +138,4 @@ class GPSStatus : public Status
} // namespace meshtastic
extern meshtastic::GPSStatus *gpsStatus;
extern meshtastic::GPSStatus *gpsStatus;
+27 -7
View File
@@ -24,11 +24,13 @@
#include "nrfx_power.h"
#endif
#ifdef DEBUG_HEAP_MQTT
#if defined(DEBUG_HEAP_MQTT) && !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h"
#include "target_specific.h"
#if !MESTASTIC_EXCLUDE_WIFI
#include <WiFi.h>
#endif
#endif
#ifndef DELAY_FOREVER
#define DELAY_FOREVER portMAX_DELAY
@@ -54,6 +56,19 @@ static const adc_atten_t atten = ADC_ATTENUATION;
#endif
#endif // BATTERY_PIN && ARCH_ESP32
#ifdef EXT_CHRG_DETECT
#ifndef EXT_CHRG_DETECT_MODE
static const uint8_t ext_chrg_detect_mode = INPUT;
#else
static const uint8_t ext_chrg_detect_mode = EXT_CHRG_DETECT_MODE;
#endif
#ifndef EXT_CHRG_DETECT_VALUE
static const uint8_t ext_chrg_detect_value = HIGH;
#else
static const uint8_t ext_chrg_detect_value = EXT_CHRG_DETECT_VALUE;
#endif
#endif
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
INA260Sensor ina260Sensor;
INA219Sensor ina219Sensor;
@@ -322,7 +337,14 @@ class AnalogBatteryLevel : public HasBatteryLevel
/// Assume charging if we have a battery and external power is connected.
/// we can't be smart enough to say 'full'?
virtual bool isCharging() override { return isBatteryConnect() && isVbusIn(); }
virtual bool isCharging() override
{
#ifdef EXT_CHRG_DETECT
return digitalRead(EXT_CHRG_DETECT) == ext_chrg_detect_value;
#else
return isBatteryConnect() && isVbusIn();
#endif
}
private:
/// If we see a battery voltage higher than physics allows - assume charger is pumping
@@ -389,6 +411,9 @@ bool Power::analogInit()
#ifdef EXT_PWR_DETECT
pinMode(EXT_PWR_DETECT, INPUT);
#endif
#ifdef EXT_CHRG_DETECT
pinMode(EXT_CHRG_DETECT, ext_chrg_detect_mode);
#endif
#ifdef BATTERY_PIN
LOG_DEBUG("Using analog input %d for battery level\n", BATTERY_PIN);
@@ -473,11 +498,6 @@ bool Power::setup()
void Power::shutdown()
{
screen->setOn(false);
#if defined(USE_EINK) && defined(PIN_EINK_EN)
digitalWrite(PIN_EINK_EN, LOW); // power off backlight first
#endif
LOG_INFO("Shutting down\n");
#ifdef HAS_PMU
+11 -10
View File
@@ -8,6 +8,7 @@
* actions to be taken upon entering or exiting each state.
*/
#include "PowerFSM.h"
#include "Default.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "configuration.h"
@@ -45,7 +46,7 @@ static void sdsEnter()
{
LOG_DEBUG("Enter state: SDS\n");
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
doDeepSleep(getConfiguredOrDefaultMs(config.power.sds_secs), false);
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false);
}
extern Power *power;
@@ -245,7 +246,6 @@ Fsm powerFSM(&stateBOOT);
void PowerFSM_setup()
{
bool isRouter = (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ? 1 : 0);
bool isInfrastructureRole = isRouter || config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER;
bool isTrackerOrSensor = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR;
@@ -343,13 +343,13 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone");
powerFSM.add_timed_transition(&stateON, &stateDARK,
getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
"Screen-on timeout");
powerFSM.add_timed_transition(&statePOWER, &stateDARK,
getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
"Screen-on timeout");
powerFSM.add_timed_transition(&stateDARK, &stateDARK,
getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
"Screen-on timeout");
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
@@ -358,12 +358,13 @@ void PowerFSM_setup()
// Don't add power saving transitions if we are a power saving tracker or sensor. Sleep will be initiatiated through the
// modules
if ((isRouter || config.power.is_power_saving) && !isTrackerOrSensor) {
powerFSM.add_timed_transition(&stateNB, isInfrastructureRole ? &stateSDS : &stateLS,
getConfiguredOrDefaultMs(config.power.min_wake_secs, default_min_wake_secs), NULL,
powerFSM.add_timed_transition(&stateNB, &stateLS,
Default::getConfiguredOrDefaultMs(config.power.min_wake_secs, default_min_wake_secs), NULL,
"Min wake timeout");
powerFSM.add_timed_transition(&stateDARK, isInfrastructureRole ? &stateSDS : &stateLS,
getConfiguredOrDefaultMs(config.power.wait_bluetooth_secs, default_wait_bluetooth_secs),
NULL, "Bluetooth timeout");
powerFSM.add_timed_transition(
&stateDARK, &stateLS,
Default::getConfiguredOrDefaultMs(config.power.wait_bluetooth_secs, default_wait_bluetooth_secs), NULL,
"Bluetooth timeout");
}
#endif
+2 -1
View File
@@ -1,3 +1,4 @@
#include "Default.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "concurrency/OSThread.h"
@@ -28,7 +29,7 @@ class PowerFSMThread : public OSThread
timeLastPowered = millis();
} else if (config.power.on_battery_shutdown_after_secs > 0 && config.power.on_battery_shutdown_after_secs != UINT32_MAX &&
millis() > (timeLastPowered +
getConfiguredOrDefaultMs(
Default::getConfiguredOrDefaultMs(
config.power.on_battery_shutdown_after_secs))) { // shutdown after 30 minutes unpowered
powerFSM.trigger(EVENT_SHUTDOWN);
}
+9 -1
View File
@@ -3,7 +3,11 @@
#include "PowerFSM.h"
#include "configuration.h"
#ifdef RP2040_SLOW_CLOCK
#define Port Serial2
#else
#define Port Serial
#endif
// Defaulting to the formerly removed phone_timeout_secs value of 15 minutes
#define SERIAL_CONNECTION_TIMEOUT (15 * 60) * 1000UL
@@ -31,6 +35,10 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con
canWrite = false; // We don't send packets to our port until it has talked to us first
// setDestination(&noopPrint); for testing, try turning off 'all' debug output and see what leaks
#ifdef RP2040_SLOW_CLOCK
Port.setTX(SERIAL2_TX);
Port.setRX(SERIAL2_RX);
#endif
Port.begin(SERIAL_BAUD);
#if defined(ARCH_NRF52) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(ARCH_RP2040)
time_t timeout = millis();
@@ -64,7 +72,7 @@ bool SerialConsole::checkIsConnected()
/**
* we override this to notice when we've received a protobuf over the serial
* stream. Then we shunt off debug serial output.
* stream. Then we shut off debug serial output.
*/
bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
{
+71 -12
View File
@@ -111,6 +111,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MCP9808_ADDR 0x18
#define INA_ADDR 0x40
#define INA_ADDR_ALTERNATE 0x41
#define INA_ADDR_WAVESHARE_UPS 0x43
#define INA3221_ADDR 0x42
#define QMC6310_ADDR 0x1C
#define QMI8658_ADDR 0x6B
@@ -160,19 +161,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
also enable HAS_ option not specifically disabled by variant.h */
#include "architecture.h"
#ifndef DEFAULT_REBOOT_SECONDS
#define DEFAULT_REBOOT_SECONDS 7
#endif
#ifndef DEFAULT_SHUTDOWN_SECONDS
#define DEFAULT_SHUTDOWN_SECONDS 2
#endif
/* Step #3: mop up with disabled values for HAS_ options not handled by the above two */
// -----------------------------------------------------------------------------
// GPS
// -----------------------------------------------------------------------------
#ifndef GPS_BAUDRATE
#define GPS_BAUDRATE 9600
#endif
#ifndef GPS_THREAD_INTERVAL
#define GPS_THREAD_INTERVAL 100
#endif
#ifndef HAS_WIFI
#define HAS_WIFI 0
#endif
@@ -221,4 +219,65 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef HW_VENDOR
#error HW_VENDOR must be defined
#endif
#endif
// -----------------------------------------------------------------------------
// Global switches to turn off features for a minimized build
// -----------------------------------------------------------------------------
// #define MESHTASTIC_MINIMIZE_BUILD 1
#ifdef MESHTASTIC_MINIMIZE_BUILD
#define MESHTASTIC_EXCLUDE_MODULES 1
#define MESHTASTIC_EXCLUDE_WIFI 1
#define MESHTASTIC_EXCLUDE_BLUETOOTH 1
#define MESHTASTIC_EXCLUDE_GPS 1
#define MESHTASTIC_EXCLUDE_SCREEN 1
#define MESHTASTIC_EXCLUDE_MQTT 1
#endif
// Turn off all optional modules
#ifdef MESHTASTIC_EXCLUDE_MODULES
#define MESHTASTIC_EXCLUDE_AUDIO 1
#define MESHTASTIC_EXCLUDE_DETECTIONSENSOR 1
#define MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR 1
#define MESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION 1
#define MESHTASTIC_EXCLUDE_PAXCOUNTER 1
#define MESHTASTIC_EXCLUDE_POWER_TELEMETRY 1
#define MESHTASTIC_EXCLUDE_RANGETEST 1
#define MESHTASTIC_EXCLUDE_REMOTEHARDWARE 1
#define MESHTASTIC_EXCLUDE_STOREFORWARD 1
#define MESHTASTIC_EXCLUDE_ATAK 1
#define MESHTASTIC_EXCLUDE_CANNEDMESSAGES 1
#define MESHTASTIC_EXCLUDE_NEIGHBORINFO 1
#define MESHTASTIC_EXCLUDE_TRACEROUTE 1
#define MESHTASTIC_EXCLUDE_WAYPOINT 1
#define MESHTASTIC_EXCLUDE_INPUTBROKER 1
#define MESHTASTIC_EXCLUDE_SERIAL 1
#endif
// // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled)
#ifdef MESHTASTIC_EXCLUDE_WIFI
#define MESHTASTIC_EXCLUDE_WEBSERVER 1
#undef HAS_WIFI
#define HAS_WIFI 0
#endif
// // Turn off Bluetooth
#ifdef MESHTASTIC_EXCLUDE_BLUETOOTH
#undef HAS_BLUETOOTH
#define HAS_BLUETOOTH 0
#endif
// // Turn off GPS
#ifdef MESHTASTIC_EXCLUDE_GPS
#undef HAS_GPS
#define HAS_GPS 0
#undef MESHTASTIC_EXCLUDE_RANGETEST
#define MESHTASTIC_EXCLUDE_RANGETEST 1
#endif
// Turn off Screen
#ifdef MESHTASTIC_EXCLUDE_SCREEN
#undef HAS_SCREEN
#define HAS_SCREEN 0
#endif
+1
View File
@@ -23,6 +23,7 @@ class ScanI2C
BME_680,
BME_280,
BMP_280,
BMP_085,
INA260,
INA219,
INA3221,
+12 -2
View File
@@ -183,8 +183,13 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
case ATECC608B_ADDR:
type = ATECC608B;
if (atecc.begin(addr.address) == true) {
#ifdef RP2040_SLOW_CLOCK
if (atecc.begin(addr.address, Wire, Serial2) == true)
#else
if (atecc.begin(addr.address) == true)
#endif
{
LOG_INFO("ATECC608B initialized\n");
} else {
LOG_WARN("ATECC608B initialization failed\n");
@@ -242,6 +247,10 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
LOG_INFO("BME-280 sensor found at address 0x%x\n", (uint8_t)addr.address);
type = BME_280;
break;
case 0x55:
LOG_INFO("BMP-085 or BMP-180 sensor found at address 0x%x\n", (uint8_t)addr.address);
type = BMP_085;
break;
default:
LOG_INFO("BMP-280 sensor found at address 0x%x\n", (uint8_t)addr.address);
type = BMP_280;
@@ -250,6 +259,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
case INA_ADDR:
case INA_ADDR_ALTERNATE:
case INA_ADDR_WAVESHARE_UPS:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0xFE), 2);
LOG_DEBUG("Register MFG_UID: 0x%x\n", registerValue);
if (registerValue == 0x5449) {
+71 -31
View File
@@ -1,7 +1,10 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_GPS
#include "Default.h"
#include "GPS.h"
#include "NodeDB.h"
#include "RTC.h"
#include "configuration.h"
#include "main.h" // pmu_found
#include "sleep.h"
#include "ubx.h"
@@ -255,19 +258,6 @@ bool GPS::setup()
if (!didSerialInit) {
#if !defined(GPS_UC6580)
#if defined(RAK4630) && defined(PIN_3V3_EN)
// If we are using the RAK4630 and we have no other peripherals on the I2C bus or module interest in 3V3_S,
// then we can safely set en_gpio turn off power to 3V3 (IO2) to hard sleep the GPS
if (rtc_found.port == ScanI2C::DeviceType::NONE && rgb_found.type == ScanI2C::DeviceType::NONE &&
accelerometer_found.port == ScanI2C::DeviceType::NONE && !moduleConfig.detection_sensor.enabled &&
!moduleConfig.telemetry.air_quality_enabled && !moduleConfig.telemetry.environment_measurement_enabled &&
config.power.device_battery_ina_address == 0 && en_gpio == 0) {
LOG_DEBUG("Since no problematic peripherals or interested modules were found, setting power save GPS_EN to pin %i\n",
PIN_3V3_EN);
en_gpio = PIN_3V3_EN;
}
#endif
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
LOG_DEBUG("Probing for GPS at %d \n", serialSpeeds[speedSelect]);
gnssModel = probe(serialSpeeds[speedSelect]);
@@ -303,6 +293,26 @@ bool GPS::setup()
// Switch to Vehicle Mode, since SoftRF enables Aviation < 2g
_serial_gps->write("$PCAS11,3*1E\r\n");
delay(250);
} else if (gnssModel == GNSS_MODEL_MTK_L76B) {
// Waveshare Pico-GPS hat uses the L76B with 9600 baud
// Initialize the L76B Chip, use GPS + GLONASS
// See note in L76_Series_GNSS_Protocol_Specification, chapter 3.29
_serial_gps->write("$PMTK353,1,1,0,0,0*2B\r\n");
// Above command will reset the GPS and takes longer before it will accept new commands
delay(1000);
// only ask for RMC and GGA (GNRMC and GNGGA)
// See note in L76_Series_GNSS_Protocol_Specification, chapter 2.1
_serial_gps->write("$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28\r\n");
delay(250);
// Enable SBAS
_serial_gps->write("$PMTK301,2*2E\r\n");
delay(250);
// Enable PPS for 2D/3D fix only
_serial_gps->write("$PMTK285,3,100*3F\r\n");
delay(250);
// Switch to Fitness Mode, for running and walking purpose with low speed (<5 m/s)
_serial_gps->write("$PMTK886,1*29\r\n");
delay(250);
} else if (gnssModel == GNSS_MODEL_UC6580) {
// The Unicore UC6580 can use a lot of sat systems, enable it to
// use GPS L1 & L5 + BDS B1I & B2a + GLONASS L1 + GALILEO E1 & E5a + SBAS
@@ -488,14 +498,6 @@ bool GPS::setup()
}
}
}
msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE);
_serial_gps->write(UBXscratch, msglen);
if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to save GNSS module configuration.\n");
} else {
LOG_INFO("GNSS module configuration saved!\n");
}
} else {
// LOG_INFO("u-blox M10 hardware found.\n");
delay(1000);
@@ -588,6 +590,13 @@ bool GPS::setup()
// BBR will survive a restart, and power off for a while, but modules with small backup
// batteries or super caps will not retain the config for a long power off time.
}
msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE);
_serial_gps->write(UBXscratch, msglen);
if (getACK(0x06, 0x09, 2000) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to save GNSS module configuration.\n");
} else {
LOG_INFO("GNSS module configuration saved!\n");
}
}
didSerialInit = true;
}
@@ -638,17 +647,27 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
return;
}
#endif
#ifdef PIN_GPS_STANDBY // Specifically the standby pin for L76K and clones
#ifdef PIN_GPS_STANDBY // Specifically the standby pin for L76B, L76K and clones
if (on) {
LOG_INFO("Waking GPS\n");
pinMode(PIN_GPS_STANDBY, OUTPUT);
// Some PCB's use an inverse logic due to a transistor driver
// Example for this is the Pico-Waveshare Lora+GPS HAT
#ifdef PIN_GPS_STANDBY_INVERTED
digitalWrite(PIN_GPS_STANDBY, 0);
#else
digitalWrite(PIN_GPS_STANDBY, 1);
#endif
return;
} else {
LOG_INFO("GPS entering sleep\n");
// notifyGPSSleep.notifyObservers(NULL);
pinMode(PIN_GPS_STANDBY, OUTPUT);
#ifdef PIN_GPS_STANDBY_INVERTED
digitalWrite(PIN_GPS_STANDBY, 1);
#else
digitalWrite(PIN_GPS_STANDBY, 0);
#endif
return;
}
#endif
@@ -742,7 +761,7 @@ uint32_t GPS::getWakeTime() const
if (t == UINT32_MAX)
return t; // already maxint
return getConfiguredOrDefaultMs(t, default_broadcast_interval_secs);
return Default::getConfiguredOrDefaultMs(t, default_broadcast_interval_secs);
}
/** Get how long we should sleep between aqusition attempts in msecs
@@ -758,7 +777,7 @@ uint32_t GPS::getSleepTime() const
if (t == UINT32_MAX)
return t; // already maxint
return t * 1000;
return Default::getConfiguredOrDefaultMs(t, default_gps_update_interval);
}
void GPS::publishUpdate()
@@ -798,7 +817,7 @@ int32_t GPS::runOnce()
LOG_WARN("GPS FactoryReset requested\n");
if (gps->factoryReset()) { // If we don't succeed try again next time
devicestate.did_gps_reset = true;
nodeDB.saveToDisk(SEGMENT_DEVICESTATE);
nodeDB->saveToDisk(SEGMENT_DEVICESTATE);
}
}
GPSInitFinished = true;
@@ -818,7 +837,7 @@ int32_t GPS::runOnce()
if (devicestate.did_gps_reset && (millis() - lastWakeStartMsec > 60000) && !hasFlow()) {
LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n");
devicestate.did_gps_reset = false;
nodeDB.saveDeviceStateToDisk();
nodeDB->saveDeviceStateToDisk();
return disable(); // Stop the GPS thread as it can do nothing useful until next reboot.
}
}
@@ -929,7 +948,7 @@ GnssModel_t GPS::probe(int serialSpeed)
uint8_t buffer[768] = {0};
delay(100);
// Close all NMEA sentences , Only valid for MTK platform
// Close all NMEA sentences , Only valid for L76K MTK platform
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20);
@@ -941,6 +960,18 @@ GnssModel_t GPS::probe(int serialSpeed)
return GNSS_MODEL_MTK;
}
// Close all NMEA sentences, valid for L76B MTK platform (Waveshare Pico GPS)
_serial_gps->write("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n");
delay(20);
// Get version information
clearBuffer();
_serial_gps->write("$PMTK605*31\r\n");
if (getACK("Quectel-L76B", 500) == GNSS_RESPONSE_OK) {
LOG_INFO("L76B GNSS init succeeded, using L76B GNSS Module\n");
return GNSS_MODEL_MTK_L76B;
}
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
UBXChecksum(cfg_rate, sizeof(cfg_rate));
clearBuffer();
@@ -1122,7 +1153,6 @@ GPS *GPS::createGps()
LOG_DEBUG("Using GPIO%d for GPS RX\n", new_gps->rx_gpio);
LOG_DEBUG("Using GPIO%d for GPS TX\n", new_gps->tx_gpio);
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, new_gps->rx_gpio, new_gps->tx_gpio);
#else
_serial_gps->begin(GPS_BAUDRATE);
#endif
@@ -1181,7 +1211,16 @@ bool GPS::factoryReset()
// byte _message_CFG_RST_COLDSTART[] = {0xB5, 0x62, 0x06, 0x04, 0x04, 0x00, 0xFF, 0xB9, 0x00, 0x00, 0xC6, 0x8B};
// _serial_gps->write(_message_CFG_RST_COLDSTART, sizeof(_message_CFG_RST_COLDSTART));
// delay(1000);
} else if (gnssModel == GNSS_MODEL_MTK) {
// send the CAS10 to perform a factory restart of the device (and other device that support PCAS statements)
LOG_INFO("GNSS Factory Reset via PCAS10,3\n");
_serial_gps->write("$PCAS10,3*1F\r\n");
delay(100);
} else {
// fire this for good measure, if we have an L76B - won't harm other devices.
_serial_gps->write("$PMTK104*37\r\n");
// No PMTK_ACK for this command.
delay(100);
// send the UBLOX Factory Reset Command regardless of detect state, something is very wrong, just assume it's UBLOX.
// Factory Reset
byte _message_reset[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFB, 0x00, 0x00, 0x00,
@@ -1241,7 +1280,7 @@ bool GPS::lookForLocation()
#ifndef TINYGPS_OPTION_NO_STATISTICS
if (reader.failedChecksum() > lastChecksumFailCount) {
LOG_WARN("Warning, %u new GPS checksum failures, for a total of %u.\n", reader.failedChecksum() - lastChecksumFailCount,
LOG_WARN("%u new GPS checksum failures, for a total of %u.\n", reader.failedChecksum() - lastChecksumFailCount,
reader.failedChecksum());
lastChecksumFailCount = reader.failedChecksum();
}
@@ -1444,4 +1483,5 @@ void GPS::toggleGpsMode()
LOG_DEBUG("Flag set to true to restore power. GpsMode: ENABLED\n");
enable();
}
}
}
#endif // Exclude GPS
+9 -8
View File
@@ -1,4 +1,6 @@
#pragma once
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPSStatus.h"
#include "Observer.h"
@@ -20,12 +22,7 @@ struct uBloxGnssModelInfo {
char extension[10][30];
};
typedef enum {
GNSS_MODEL_MTK,
GNSS_MODEL_UBLOX,
GNSS_MODEL_UC6580,
GNSS_MODEL_UNKNOWN,
} GnssModel_t;
typedef enum { GNSS_MODEL_MTK, GNSS_MODEL_UBLOX, GNSS_MODEL_UC6580, GNSS_MODEL_UNKNOWN, GNSS_MODEL_MTK_L76B } GnssModel_t;
typedef enum {
GNSS_RESPONSE_NONE,
@@ -92,8 +89,11 @@ class GPS : private concurrency::OSThread
public:
/** If !NULL we will use this serial port to construct our GPS */
#if defined(RPI_PICO_WAVESHARE)
static SerialUART *_serial_gps;
#else
static HardwareSerial *_serial_gps;
#endif
static uint8_t _message_PMREQ[];
static uint8_t _message_PMREQ_10[];
static const uint8_t _message_CFG_RXM_PSM[];
@@ -270,4 +270,5 @@ class GPS : private concurrency::OSThread
GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN;
};
extern GPS *gps;
extern GPS *gps;
#endif // Exclude GPS
+1 -1
View File
@@ -11,7 +11,7 @@
#define PI 3.1415926535897932384626433832795
#define OLC_CODE_LEN 11
#define DEG_CONVERT 180 / PI
#define DEG_CONVERT (180 / PI)
// Helper functions
// Raises a number to an exponent, handling negative exponents.
+4 -1
View File
@@ -1,3 +1,4 @@
#if !MESHTASTIC_EXCLUDE_GPS
#include "NMEAWPL.h"
#include "GeoCoord.h"
#include "RTC.h"
@@ -93,4 +94,6 @@ uint32_t printGGA(char *buf, size_t bufsz, const meshtastic_Position &pos)
}
len += snprintf(buf + len, bufsz - len, "*%02X\r\n", chk);
return len;
}
}
#endif
+55 -378
View File
@@ -2,127 +2,49 @@
#ifdef USE_EINK
#include "EInkDisplay2.h"
#include "GxEPD2_BW.h"
#include "SPILock.h"
#include "main.h"
#include <SPI.h>
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0)
SPIClass *hspi = NULL;
#endif
/*
The macros EINK_DISPLAY_MODEL, EINK_WIDTH, and EINK_HEIGHT are defined as build_flags in a variant's platformio.ini
Previously, these macros were defined at the top of this file.
#define COLORED GxEPD_BLACK
#define UNCOLORED GxEPD_WHITE
For archival reasons, note that the following configurations had also been tested during this period:
* ifdef RAK4631
- 4.2 inch
EINK_DISPLAY_MODEL: GxEPD2_420_M01
EINK_WIDTH: 300
EINK_WIDTH: 400
#if defined(TTGO_T_ECHO)
#define TECHO_DISPLAY_MODEL GxEPD2_154_D67
#elif defined(RAK4630)
- 2.9 inch
EINK_DISPLAY_MODEL: GxEPD2_290_T5D
EINK_WIDTH: 296
EINK_HEIGHT: 128
// GxEPD2_213_BN - RAK14000 2.13 inch b/w 250x122 - changed from GxEPD2_213_B74 - which was not going to give fast refresh
// support
#define TECHO_DISPLAY_MODEL GxEPD2_213_BN
// 4.2 inch 300x400 - GxEPD2_420_M01
// #define TECHO_DISPLAY_MODEL GxEPD2_420_M01
// 2.9 inch 296x128 - GxEPD2_290_T5D
// #define TECHO_DISPLAY_MODEL GxEPD2_290_T5D
// 1.54 inch 200x200 - GxEPD2_154_M09
// #define TECHO_DISPLAY_MODEL GxEPD2_154_M09
#elif defined(MAKERPYTHON)
// 2.9 inch 296x128 - GxEPD2_290_T5D
#define TECHO_DISPLAY_MODEL GxEPD2_290_T5D
#elif defined(PCA10059)
// 4.2 inch 300x400 - GxEPD2_420_M01
#define TECHO_DISPLAY_MODEL GxEPD2_420_M01
#elif defined(M5_COREINK)
// M5Stack CoreInk
// 1.54 inch 200x200 - GxEPD2_154_M09
#define TECHO_DISPLAY_MODEL GxEPD2_154_M09
#elif defined(HELTEC_WIRELESS_PAPER)
// #define TECHO_DISPLAY_MODEL GxEPD2_213_T5D
#define TECHO_DISPLAY_MODEL GxEPD2_213_FC1
#elif defined(HELTEC_WIRELESS_PAPER_V1_0)
// 2.13" 122x250 - DEPG0213BNS800
#define TECHO_DISPLAY_MODEL GxEPD2_213_BN
#endif
GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT> *adafruitDisplay;
- 1.54 inch
EINK_DISPLAY_MODEL: GxEPD2_154_M09
EINK_WIDTH: 200
EINK_HEIGHT: 200
*/
// Constructor
EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus)
{
#if defined(TTGO_T_ECHO)
setGeometry(GEOMETRY_RAWMODE, 200, 200);
#elif defined(RAK4630)
// GxEPD2_213_BN - RAK14000 2.13 inch b/w 250x122
setGeometry(GEOMETRY_RAWMODE, 250, 122);
this->displayBufferSize = 250 * (128 / 8);
// GxEPD2_420_M01
// setGeometry(GEOMETRY_RAWMODE, 300, 400);
// GxEPD2_290_T5D
// setGeometry(GEOMETRY_RAWMODE, 296, 128);
// GxEPD2_154_M09
// setGeometry(GEOMETRY_RAWMODE, 200, 200);
#elif defined(HELTEC_WIRELESS_PAPER_V1_0)
// The display's memory is actually 128px x 250px
// Setting the buffersize manually prevents 122/8 truncating to a 15 byte width
// (Or something like that..)
// Set dimensions in OLEDDisplay base class
this->geometry = GEOMETRY_RAWMODE;
this->displayWidth = 250;
this->displayHeight = 122;
this->displayBufferSize = 250 * (128 / 8);
this->displayWidth = EINK_WIDTH;
this->displayHeight = EINK_HEIGHT;
#elif defined(HELTEC_WIRELESS_PAPER)
// GxEPD2_213_BN - 2.13 inch b/w 250x122
setGeometry(GEOMETRY_RAWMODE, 250, 122);
#elif defined(MAKERPYTHON)
// GxEPD2_290_T5D
setGeometry(GEOMETRY_RAWMODE, 296, 128);
// Round shortest side up to nearest byte, to prevent truncation causing an undersized buffer
uint16_t shortSide = min(EINK_WIDTH, EINK_HEIGHT);
uint16_t longSide = max(EINK_WIDTH, EINK_HEIGHT);
if (shortSide % 8 != 0)
shortSide = (shortSide | 7) + 1;
#elif defined(PCA10059)
// GxEPD2_420_M01
setGeometry(GEOMETRY_RAWMODE, 300, 400);
#elif defined(M5_COREINK)
// M5Stack_CoreInk 200x200
// 1.54 inch 200x200 - GxEPD2_154_M09
setGeometry(GEOMETRY_RAWMODE, EPD_HEIGHT, EPD_WIDTH);
#elif defined(my)
// GxEPD2_290_T5D
setGeometry(GEOMETRY_RAWMODE, 296, 128);
LOG_DEBUG("GEOMETRY_RAWMODE, 296, 128\n");
#elif defined(ESP32_S3_PICO)
// GxEPD2_290_T94_V2
setGeometry(GEOMETRY_RAWMODE, EPD_WIDTH, EPD_HEIGHT);
LOG_DEBUG("GEOMETRY_RAWMODE, 296, 128\n");
#endif
// setGeometry(GEOMETRY_RAWMODE, 128, 64); // old resolution
// setGeometry(GEOMETRY_128_64); // We originally used this because I wasn't sure if rawmode worked - it does
this->displayBufferSize = longSide * (shortSide / 8);
}
// FIXME quick hack to limit drawing to a very slow rate
uint32_t lastDrawMsec;
/**
* Force a display update if we haven't drawn within the specified msecLimit
*/
@@ -131,13 +53,6 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
// No need to grab this lock because we are on our own SPI bus
// concurrency::LockGuard g(spiLock);
#if defined(USE_EINK_DYNAMIC_REFRESH)
// Decide between full refresh, fast refresh, or skipping the update
bool continueUpdate = determineRefreshMode();
if (!continueUpdate)
return false;
#else
uint32_t now = millis();
uint32_t sinceLast = now - lastDrawMsec;
@@ -146,56 +61,34 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
else
return false;
#endif
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
// tft.drawBitmap(0, 0, buffer, 128, 64, TFT_YELLOW, TFT_BLACK);
for (uint32_t y = 0; y < displayHeight; y++) {
for (uint32_t x = 0; x < displayWidth; x++) {
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficient
auto b = buffer[x + (y / 8) * displayWidth];
auto isset = b & (1 << (y & 7));
adafruitDisplay->drawPixel(x, y, isset ? COLORED : UNCOLORED);
adafruitDisplay->drawPixel(x, y, isset ? GxEPD_BLACK : GxEPD_WHITE);
}
}
// Trigger the refresh in GxEPD2
LOG_DEBUG("Updating E-Paper... ");
#if defined(TTGO_T_ECHO)
adafruitDisplay->nextPage();
#elif defined(RAK4630) || defined(MAKERPYTHON)
// RAK14000 2.13 inch b/w 250x122 actually now does support fast refresh
// Full update mode (slow)
// adafruitDisplay->display(false); // FIXME, use fast refresh mode
// Only enable for e-Paper with support for fast updates and comment out above adafruitDisplay->display(false);
// 1.54 inch 200x200 - GxEPD2_154_M09
// 2.13 inch 250x122 - GxEPD2_213_BN
// 2.9 inch 296x128 - GxEPD2_290_T5D
// 4.2 inch 300x400 - GxEPD2_420_M01
adafruitDisplay->nextPage();
#elif defined(PCA10059) || defined(M5_COREINK)
adafruitDisplay->nextPage();
#elif defined(HELTEC_WIRELESS_PAPER_V1_0)
adafruitDisplay->nextPage();
#elif defined(HELTEC_WIRELESS_PAPER)
adafruitDisplay->nextPage();
#elif defined(ESP32_S3_PICO)
adafruitDisplay->nextPage();
#elif defined(PRIVATE_HW) || defined(my)
adafruitDisplay->nextPage();
#endif
// End the update process
endUpdate();
// Put screen to sleep to save power (possibly not necessary because we already did poweroff inside of display)
adafruitDisplay->hibernate();
LOG_DEBUG("done\n");
return true;
}
// End the update process - virtual method, overriden in derived class
void EInkDisplay::endUpdate()
{
// Power off display hardware, then deep-sleep (Except Wireless Paper V1.1, no deep-sleep)
adafruitDisplay->hibernate();
}
// Write the buffer to the display memory
void EInkDisplay::display(void)
{
@@ -203,15 +96,9 @@ void EInkDisplay::display(void)
// at least one forceDisplay() keyframe. This prevents flashing when we should the critical
// bootscreen (that we want to look nice)
#ifdef USE_EINK_DYNAMIC_REFRESH
lowPriority();
forceDisplay();
highPriority();
#else
if (lastDrawMsec) {
forceDisplay(slowUpdateMsec); // Show the first screen a few seconds after boot, then slower
}
#endif
}
// Send a command to the display (low level function)
@@ -226,16 +113,11 @@ void EInkDisplay::setDetected(uint8_t detected)
(void)detected;
}
// Connect to the display
// Connect to the display - variant specific
bool EInkDisplay::connect()
{
LOG_INFO("Doing EInk init\n");
#ifdef PIN_EINK_PWR_ON
pinMode(PIN_EINK_PWR_ON, OUTPUT);
digitalWrite(PIN_EINK_PWR_ON, HIGH); // If we need to assert a pin to power external peripherals
#endif
#ifdef PIN_EINK_EN
// backlight power, HIGH is backlight on, LOW is off
pinMode(PIN_EINK_EN, OUTPUT);
@@ -244,9 +126,9 @@ bool EInkDisplay::connect()
#if defined(TTGO_T_ECHO)
{
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init();
adafruitDisplay->setRotation(3);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
@@ -254,8 +136,8 @@ bool EInkDisplay::connect()
#elif defined(RAK4630) || defined(MAKERPYTHON)
{
if (eink_found) {
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
// RAK14000 2.13 inch b/w 250x122 does actually now support fast refresh
adafruitDisplay->setRotation(3);
@@ -267,7 +149,7 @@ bool EInkDisplay::connect()
}
}
#elif defined(HELTEC_WIRELESS_PAPER_V1_0)
#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER)
{
// Is this a normal boot, or a wake from deep sleep?
esp_sleep_wakeup_cause_t wakeReason = esp_sleep_get_wakeup_cause();
@@ -296,243 +178,38 @@ bool EInkDisplay::connect()
delay(100);
// Create GxEPD2 objects
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
// Init GxEPD2
adafruitDisplay->init();
adafruitDisplay->setRotation(3);
}
#elif defined(HELTEC_WIRELESS_PAPER)
{
hspi = new SPIClass(HSPI);
hspi->begin(PIN_EINK_SCLK, -1, PIN_EINK_MOSI, PIN_EINK_CS); // SCLK, MISO, MOSI, SS
delay(100);
pinMode(Vext, OUTPUT);
digitalWrite(Vext, LOW);
delay(100);
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, *hspi);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init();
adafruitDisplay->setRotation(3);
}
#elif defined(PCA10059)
{
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
adafruitDisplay->setRotation(3);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
}
#elif defined(M5_COREINK)
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
adafruitDisplay->setRotation(0);
adafruitDisplay->setPartialWindow(0, 0, EPD_WIDTH, EPD_HEIGHT);
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
#elif defined(my) || defined(ESP32_S3_PICO)
{
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 40, false, SPI, SPISettings(4000000, MSBFIRST, SPI_MODE0));
adafruitDisplay->setRotation(1);
adafruitDisplay->setPartialWindow(0, 0, EPD_WIDTH, EPD_HEIGHT);
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
}
#endif
// adafruitDisplay->setFullWindow();
// adafruitDisplay->fillScreen(UNCOLORED);
// adafruitDisplay->drawCircle(100, 100, 20, COLORED);
// adafruitDisplay->display(false);
return true;
}
// Use a mix of full refresh, fast refresh, and update skipping, to balance urgency and display health
#if defined(USE_EINK_DYNAMIC_REFRESH)
// Suggest that subsequent updates should use fast-refresh
void EInkDisplay::highPriority()
{
isHighPriority = true;
}
// Suggest that subsequent updates should use full-refresh
void EInkDisplay::lowPriority()
{
isHighPriority = false;
}
// Full-refresh is explicitly requested for next one update - no skipping please
void EInkDisplay::demandFullRefresh()
{
demandingFull = true;
}
// configure display for fast-refresh
void EInkDisplay::configForFastRefresh()
{
// Display-specific code can go here
#if defined(PRIVATE_HW)
#else
// Otherwise:
adafruitDisplay->setPartialWindow(0, 0, adafruitDisplay->width(), adafruitDisplay->height());
#endif
}
// Configure display for full-refresh
void EInkDisplay::configForFullRefresh()
{
// Display-specific code can go here
#if defined(PRIVATE_HW)
#else
// Otherwise:
adafruitDisplay->setFullWindow();
#endif
}
#ifdef EINK_FASTREFRESH_ERASURE_LIMIT
// Count black pixels in an image. Used for "erasure tracking"
int32_t EInkDisplay::countBlackPixels()
{
int32_t blackCount = 0; // Signed, to avoid underflow when comparing
for (uint16_t b = 0; b < (displayWidth / 8) * displayHeight; b++) {
for (uint8_t i = 0; i < 7; i++) {
// Check if each bit is black or white
blackCount += (buffer[b] >> i) & 1;
}
}
return blackCount;
}
// Evaluate the (rough) amount of black->white pixel change since last full refresh
bool EInkDisplay::tooManyErasures()
{
// Ideally, we would compare the new and old buffers, to count *actual* white-to-black pixel changes
// but that would require substantially more "code tampering"
// Get the black pixel stats for this image
int32_t blackCount = countBlackPixels();
int32_t blackDifference = blackCount - prevBlackCount;
// Update the running total of "erasures" - black pixels which have become white, since last full-refresh
if (blackDifference < 0)
erasedSinceFull -= blackDifference;
// Store black pixel count for next time
prevBlackCount = blackCount;
// Log the running total - help devs setup new boards
LOG_DEBUG("Dynamic Refresh: erasedSinceFull=%hu, EINK_FASTREFRESH_ERASURE_LIMIT=%hu\n", erasedSinceFull,
EINK_FASTREFRESH_ERASURE_LIMIT);
// Check if too many pixels have been erased
if (erasedSinceFull > EINK_FASTREFRESH_ERASURE_LIMIT)
return true; // Too many
else
return false; // Still okay
}
#endif // ifdef EINK_FASTREFRESH_ERASURE_LIMIT
bool EInkDisplay::newImageMatchesOld()
{
uint32_t newImageHash = 0;
// Generate hash: sum all bytes in the image buffer
for (uint16_t b = 0; b < (displayWidth / 8) * displayHeight; b++) {
newImageHash += buffer[b];
}
// Compare hashes
bool hashMatches = (newImageHash == prevImageHash);
// Update the cached hash
prevImageHash = newImageHash;
// Return the comparison result
return hashMatches;
}
// Choose between, full-refresh, fast refresh, and update skipping, to balance urgency and display health.
bool EInkDisplay::determineRefreshMode()
{
uint32_t now = millis();
uint32_t sinceLast = now - lastUpdateMsec;
// If rate-limiting dropped a high-priority update:
// promote this update, so it runs ASAP
if (missedHighPriorityUpdate) {
isHighPriority = true;
missedHighPriorityUpdate = false;
}
// Abort: if too soon for a new frame (unless demanding full)
if (!demandingFull && isHighPriority && fastRefreshCount > 0 && sinceLast < highPriorityLimitMsec) {
LOG_DEBUG("Dynamic Refresh: update skipped. Exceeded EINK_HIGHPRIORITY_LIMIT_SECONDS\n");
missedHighPriorityUpdate = true;
return false;
}
if (!demandingFull && !isHighPriority && !demandingFull && sinceLast < lowPriorityLimitMsec) {
return false;
}
// If demanded full refresh: give it to them
if (demandingFull)
needsFull = true;
// Check if old image (fast-refresh) should be redrawn (as full), for image quality
if (fastRefreshCount > 0 && !isHighPriority)
needsFull = true;
// If too many fast updates, require a full-refresh (display health)
if (fastRefreshCount >= fastRefreshLimit)
needsFull = true;
#ifdef EINK_FASTREFRESH_ERASURE_LIMIT
// Some displays struggle with erasing black pixels to white, during fast-refresh
if (tooManyErasures())
needsFull = true;
#endif
// If image matches
// (Block must run, even if full already selected, to store hash for next time)
if (newImageMatchesOld()) {
// If low priority: limit rate
// otherwise, every loop() will run the hash method
if (!isHighPriority)
lastUpdateMsec = now;
// If update is *not* for display health or image quality, skip it
if (!needsFull)
return false;
}
// Conditions assessed - not skipping - load the appropriate config
// If options require a full refresh
if (!isHighPriority || needsFull) {
if (fastRefreshCount > 0)
configForFullRefresh();
LOG_DEBUG("Dynamic Refresh: conditions met for full-refresh\n");
fastRefreshCount = 0;
needsFull = false;
demandingFull = false;
erasedSinceFull = 0; // Reset the count for EINK_FASTREFRESH_ERASURE_LIMIT - tracks ghosting buildup
}
// If options allow a fast-refresh
else {
if (fastRefreshCount == 0)
configForFastRefresh();
LOG_DEBUG("Dynamic Refresh: conditions met for fast-refresh\n");
fastRefreshCount++;
}
lastUpdateMsec = now; // Mark time for rate limiting
return true; // Instruct calling method to continue with update
}
#endif // End USE_EINK_DYNAMIC_REFRESH
#endif
+25 -75
View File
@@ -1,8 +1,11 @@
#pragma once
#ifdef USE_EINK
#include "GxEPD2_BW.h"
#include <OLEDDisplay.h>
#if defined(HELTEC_WIRELESS_PAPER_V1_0)
#if defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER)
// Re-enable SPI after deep sleep: rtc_gpio_hold_dis()
#include "driver/rtc_io.h"
#endif
@@ -10,12 +13,15 @@
/**
* An adapter class that allows using the GxEPD2 library as if it was an OLEDDisplay implementation.
*
* Note: EInkDynamicDisplay derives from this class.
*
* Remaining TODO:
* optimize display() to only draw changed pixels (see other OLED subclasses for examples)
* implement displayOn/displayOff to turn off the TFT device (and backlight)
* Use the fast NRF52 SPI API rather than the slow standard arduino version
*
* turn radio back on - currently with both on spi bus is fucked? or are we leaving chip select asserted?
* Suggestion: perhaps similar to HELTEC_WIRELESS_PAPER issue, which resolved with rtc_gpio_hold_dis()
*/
class EInkDisplay : public OLEDDisplay
{
@@ -37,7 +43,14 @@ class EInkDisplay : public OLEDDisplay
*
* @return true if we did draw the screen
*/
bool forceDisplay(uint32_t msecLimit = 1000);
virtual bool forceDisplay(uint32_t msecLimit = 1000);
/**
* Run any code needed to complete an update, after the physical refresh has completed.
* Split from forceDisplay(), to enable async refresh in derived EInkDynamicDisplay class.
*
*/
virtual void endUpdate();
/**
* shim to make the abstraction happy
@@ -55,80 +68,17 @@ class EInkDisplay : public OLEDDisplay
// Connect to the display
virtual bool connect() override;
#if defined(USE_EINK_DYNAMIC_REFRESH)
// Full, fast, or skip: balance urgency with display health
// AdafruitGFX display object - instantiated in connect(), variant specific
GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT> *adafruitDisplay = NULL;
// Use fast refresh if EITHER:
// * highPriority() was set
// * a highPriority() update was previously skipped, for rate-limiting - (EINK_HIGHPRIORITY_LIMIT_SECONDS)
// Use full refresh if EITHER:
// * lowPriority() was set
// * demandFullRefresh() was called - (single shot)
// * too many fast updates in a row: protect display - (EINK_FASTREFRESH_REPEAT_LIMIT)
// * no recent updates, and last update was fast: redraw for image quality (EINK_LOWPRIORITY_LIMIT_SECONDS)
// * (optional) too many "erasures" since full-refresh (black pixels cleared to white)
// Rate limit if:
// * lowPriority() - (EINK_LOWPRIORITY_LIMIT_SECONDS)
// * highPriority(), if multiple fast updates have run back-to-back - (EINK_HIGHPRIORITY_LIMIT_SECONDS)
// Skip update entirely if ALL criteria met:
// * new image matches old image
// * lowPriority()
// * no call to demandFullRefresh()
// * not redrawing for image quality
// * not refreshing for display health
// ------------------------------------
// To implement for your E-Ink display:
// * edit configForFastRefresh()
// * edit configForFullRefresh()
// * add macros to variant.h, and adjust to taste:
/*
#define USE_EINK_DYNAMIC_REFRESH
#define EINK_LOWPRIORITY_LIMIT_SECONDS 30
#define EINK_HIGHPRIORITY_LIMIT_SECONDS 1
#define EINK_FASTREFRESH_REPEAT_LIMIT 5
#define EINK_FASTREFRESH_ERASURE_LIMIT 300 // optional
*/
public:
void highPriority(); // Suggest fast refresh
void lowPriority(); // Suggest full refresh
void demandFullRefresh(); // For next update: explicitly request full refresh
protected:
void configForFastRefresh(); // Display specific code to select fast refresh mode
void configForFullRefresh(); // Display specific code to return to full refresh mode
bool newImageMatchesOld(); // Is the new update actually different to the last image?
bool determineRefreshMode(); // Called immediately before data written to display - choose refresh mode, or abort update
#ifdef EINK_FASTREFRESH_ERASURE_LIMIT
int32_t countBlackPixels(); // Calculate the number of black pixels in the new image
bool tooManyErasures(); // Has too much "ghosting" (black pixels erased to white) accumulated since last full-refresh?
// If display uses HSPI
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0)
SPIClass *hspi = NULL;
#endif
bool isHighPriority = true; // Does the method calling update believe that this is urgent?
bool needsFull = false; // Is a full refresh forced? (display health)
bool demandingFull = false; // Was full refresh specifically requested? (splash screens, etc)
bool missedHighPriorityUpdate = false; // Was a high priority update skipped for rate-limiting?
uint16_t fastRefreshCount = 0; // How many fast updates have occurred since last full refresh?
uint32_t lastUpdateMsec = 0; // When did the last update occur?
uint32_t prevImageHash = 0; // Used to check if update will change screen image (skippable or not)
int32_t prevBlackCount = 0; // How many black pixels were in the previous image
uint32_t erasedSinceFull = 0; // How many black pixels have been set back to white since last full-refresh? (roughly)
// Set in variant.h
const uint32_t lowPriorityLimitMsec = (uint32_t)1000 * EINK_LOWPRIORITY_LIMIT_SECONDS; // Max rate for fast refreshes
const uint32_t highPriorityLimitMsec = (uint32_t)1000 * EINK_HIGHPRIORITY_LIMIT_SECONDS; // Max rate for full refreshes
const uint32_t fastRefreshLimit = EINK_FASTREFRESH_REPEAT_LIMIT; // Max consecutive fast updates, before full is triggered
#else // !USE_EINK_DYNAMIC_REFRESH
// Tolerate calls to these methods anywhere, just to be safe
void highPriority() {}
void lowPriority() {}
void demandFullRefresh() {}
#endif
private:
// FIXME quick hack to limit drawing to a very slow rate
uint32_t lastDrawMsec = 0;
};
#endif
+530
View File
@@ -0,0 +1,530 @@
#include "configuration.h"
#if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY)
#include "EInkDynamicDisplay.h"
// Constructor
EInkDynamicDisplay::EInkDynamicDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus)
: EInkDisplay(address, sda, scl, geometry, i2cBus), NotifiedWorkerThread("EInkDynamicDisplay")
{
// If tracking ghost pixels, grab memory
#ifdef EINK_LIMIT_GHOSTING_PX
dirtyPixels = new uint8_t[EInkDisplay::displayBufferSize](); // Init with zeros
#endif
}
// Destructor
EInkDynamicDisplay::~EInkDynamicDisplay()
{
// If we were tracking ghost pixels, free the memory
#ifdef EINK_LIMIT_GHOSTING_PX
delete[] dirtyPixels;
#endif
}
// Screen requests a BACKGROUND frame
void EInkDynamicDisplay::display()
{
addFrameFlag(BACKGROUND);
update();
}
// Screen requests a RESPONSIVE frame
bool EInkDynamicDisplay::forceDisplay(uint32_t msecLimit)
{
addFrameFlag(RESPONSIVE);
return update(); // (Unutilized) Base class promises to return true if update ran
}
// Add flag for the next frame
void EInkDynamicDisplay::addFrameFlag(frameFlagTypes flag)
{
// OR the new flag into the existing flags
this->frameFlags = (frameFlagTypes)(this->frameFlags | flag);
}
// GxEPD2 code to set fast refresh
void EInkDynamicDisplay::configForFastRefresh()
{
// Variant-specific code can go here
#if defined(PRIVATE_HW)
#else
// Otherwise:
adafruitDisplay->setPartialWindow(0, 0, adafruitDisplay->width(), adafruitDisplay->height());
#endif
}
// GxEPD2 code to set full refresh
void EInkDynamicDisplay::configForFullRefresh()
{
// Variant-specific code can go here
#if defined(PRIVATE_HW)
#else
// Otherwise:
adafruitDisplay->setFullWindow();
#endif
}
// Run any relevant GxEPD2 code, so next update will use correct refresh type
void EInkDynamicDisplay::applyRefreshMode()
{
// Change from FULL to FAST
if (currentConfig == FULL && refresh == FAST) {
configForFastRefresh();
currentConfig = FAST;
}
// Change from FAST back to FULL
else if (currentConfig == FAST && refresh == FULL) {
configForFullRefresh();
currentConfig = FULL;
}
}
// Update fastRefreshCount
void EInkDynamicDisplay::adjustRefreshCounters()
{
if (refresh == FAST)
fastRefreshCount++;
else if (refresh == FULL)
fastRefreshCount = 0;
}
// Trigger the display update by calling base class
bool EInkDynamicDisplay::update()
{
// Detemine the refresh mode to use, and start the update
bool refreshApproved = determineMode();
if (refreshApproved) {
EInkDisplay::forceDisplay(0); // Bypass base class' own rate-limiting system
storeAndReset(); // Store the result of this loop for next time. Note: call *before* endOrDetach()
endOrDetach(); // endUpdate() right now, or set the async refresh flag (if FULL and HAS_EINK_ASYNCFULL)
} else
storeAndReset(); // No update, no post-update code, just store the results
return refreshApproved; // (Unutilized) Base class promises to return true if update ran
}
// Figure out who runs the post-update code
void EInkDynamicDisplay::endOrDetach()
{
// If the GxEPD2 version reports that it has the async modifications
#ifdef HAS_EINK_ASYNCFULL
if (previousRefresh == FULL) {
asyncRefreshRunning = true; // Set the flag - checked in determineMode(); cleared by onNotify()
if (previousFrameFlags & BLOCKING)
awaitRefresh();
else {
// Async begins
LOG_DEBUG("Async full-refresh begins (dropping frames)\n");
notifyLater(intervalPollAsyncRefresh, DUE_POLL_ASYNCREFRESH, true); // Hand-off to NotifiedWorkerThread
}
}
// Fast Refresh
else if (previousRefresh == FAST)
EInkDisplay::endUpdate(); // Still block while updating, but EInkDisplay needs us to call endUpdate() ourselves.
// Fallback - If using an unmodified version of GxEPD2 for some reason
#else
if (previousRefresh == FULL || previousRefresh == FAST) { // If refresh wasn't skipped (on unspecified..)
LOG_WARN(
"GxEPD2 version has not been modified to support async refresh; using fallback behavior. Please update lib_deps in "
"variant's platformio.ini file\n");
EInkDisplay::endUpdate();
}
#endif
}
// Assess situation, pick a refresh type
bool EInkDynamicDisplay::determineMode()
{
checkInitialized();
checkForPromotion();
#if defined(HAS_EINK_ASYNCFULL)
checkBusyAsyncRefresh();
#endif
checkRateLimiting();
// If too soon for a new frame, or display busy, abort early
if (refresh == SKIPPED)
return false; // No refresh
// -- New frame is due --
resetRateLimiting(); // Once determineMode() ends, will have to wait again
hashImage(); // Generate here, so we can still copy it to previousImageHash, even if we skip the comparison check
LOG_DEBUG("determineMode(): "); // Begin log entry
// Once mode determined, any remaining checks will bypass
checkCosmetic();
checkDemandingFast();
checkFrameMatchesPrevious();
checkConsecutiveFastRefreshes();
#ifdef EINK_LIMIT_GHOSTING_PX
checkExcessiveGhosting();
#endif
checkFastRequested();
if (refresh == UNSPECIFIED)
LOG_WARN("There was a flaw in the determineMode() logic.\n");
// -- Decision has been reached --
applyRefreshMode();
adjustRefreshCounters();
#ifdef EINK_LIMIT_GHOSTING_PX
// Full refresh clears any ghosting
if (refresh == FULL)
resetGhostPixelTracking();
#endif
// Return - call a refresh or not?
if (refresh == SKIPPED)
return false; // Don't trigger a refresh
else
return true; // Do trigger a refresh
}
// Is this the very first frame?
void EInkDynamicDisplay::checkInitialized()
{
if (!initialized) {
// Undo GxEPD2_BW::partialWindow(), if set by developer in EInkDisplay::connect()
configForFullRefresh();
// Clear any existing image, so we can draw logo with fast-refresh, but also to set GxEPD2_EPD::_initial_write
adafruitDisplay->clearScreen();
LOG_DEBUG("initialized, ");
initialized = true;
// Use a fast-refresh for the next frame; no skipping or else blank screen when waking from deep sleep
addFrameFlag(DEMAND_FAST);
}
}
// Was a frame skipped (rate, display busy) that should have been a FAST refresh?
void EInkDynamicDisplay::checkForPromotion()
{
// If a frame was skipped (rate, display busy), then promote a BACKGROUND frame
// Because we DID want a RESPONSIVE/COSMETIC/DEMAND_FULL frame last time, we just didn't get it
switch (previousReason) {
case ASYNC_REFRESH_BLOCKED_DEMANDFAST:
addFrameFlag(DEMAND_FAST);
break;
case ASYNC_REFRESH_BLOCKED_COSMETIC:
addFrameFlag(COSMETIC);
break;
case ASYNC_REFRESH_BLOCKED_RESPONSIVE:
case EXCEEDED_RATELIMIT_FAST:
addFrameFlag(RESPONSIVE);
break;
default:
break;
}
}
// Is it too soon for another frame of this type?
void EInkDynamicDisplay::checkRateLimiting()
{
uint32_t now = millis();
// Sanity check: millis() overflow - just let the update run..
if (previousRunMs > now)
return;
// Skip update: too soon for BACKGROUND
if (frameFlags == BACKGROUND) {
if (now - previousRunMs < EINK_LIMIT_RATE_BACKGROUND_SEC * 1000) {
refresh = SKIPPED;
reason = EXCEEDED_RATELIMIT_FULL;
return;
}
}
// No rate-limit for these special cases
if (frameFlags & COSMETIC || frameFlags & DEMAND_FAST)
return;
// Skip update: too soon for RESPONSIVE
if (frameFlags & RESPONSIVE) {
if (now - previousRunMs < EINK_LIMIT_RATE_RESPONSIVE_SEC * 1000) {
refresh = SKIPPED;
reason = EXCEEDED_RATELIMIT_FAST;
LOG_DEBUG("refresh=SKIPPED, reason=EXCEEDED_RATELIMIT_FAST, frameFlags=0x%x\n", frameFlags);
return;
}
}
}
// Is this frame COSMETIC (splash screens?)
void EInkDynamicDisplay::checkCosmetic()
{
// If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED)
return;
// A full refresh is requested for cosmetic purposes: we have a decision
if (frameFlags & COSMETIC) {
refresh = FULL;
reason = FLAGGED_COSMETIC;
LOG_DEBUG("refresh=FULL, reason=FLAGGED_COSMETIC, frameFlags=0x%x\n", frameFlags);
}
}
// Is this a one-off special circumstance, where we REALLY want a fast refresh?
void EInkDynamicDisplay::checkDemandingFast()
{
// If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED)
return;
// A fast refresh is demanded: we have a decision
if (frameFlags & DEMAND_FAST) {
refresh = FAST;
reason = FLAGGED_DEMAND_FAST;
LOG_DEBUG("refresh=FAST, reason=FLAGGED_DEMAND_FAST, frameFlags=0x%x\n", frameFlags);
}
}
// Does the new frame match the currently displayed image?
void EInkDynamicDisplay::checkFrameMatchesPrevious()
{
// If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED)
return;
// If frame is *not* a duplicate, abort the check
if (imageHash != previousImageHash)
return;
#if !defined(EINK_BACKGROUND_USES_FAST)
// If BACKGROUND, and last update was FAST: redraw the same image in FULL (for display health + image quality)
if (frameFlags == BACKGROUND && fastRefreshCount > 0) {
refresh = FULL;
reason = REDRAW_WITH_FULL;
LOG_DEBUG("refresh=FULL, reason=REDRAW_WITH_FULL, frameFlags=0x%x\n", frameFlags);
return;
}
#endif
// Not redrawn, not COSMETIC, not DEMAND_FAST
refresh = SKIPPED;
reason = FRAME_MATCHED_PREVIOUS;
LOG_DEBUG("refresh=SKIPPED, reason=FRAME_MATCHED_PREVIOUS, frameFlags=0x%x\n", frameFlags);
}
// Have too many fast-refreshes occured consecutively, since last full refresh?
void EInkDynamicDisplay::checkConsecutiveFastRefreshes()
{
// If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED)
return;
// If too many FAST refreshes consecutively - force a FULL refresh
if (fastRefreshCount >= EINK_LIMIT_FASTREFRESH) {
refresh = FULL;
reason = EXCEEDED_LIMIT_FASTREFRESH;
LOG_DEBUG("refresh=FULL, reason=EXCEEDED_LIMIT_FASTREFRESH, frameFlags=0x%x\n", frameFlags);
}
}
// No objections, we can perform fast-refresh, if desired
void EInkDynamicDisplay::checkFastRequested()
{
if (refresh != UNSPECIFIED)
return;
if (frameFlags == BACKGROUND) {
#ifdef EINK_BACKGROUND_USES_FAST
// If we want BACKGROUND to use fast. (FULL only when a limit is hit)
refresh = FAST;
reason = BACKGROUND_USES_FAST;
LOG_DEBUG("refresh=FAST, reason=BACKGROUND_USES_FAST, fastRefreshCount=%lu, frameFlags=0x%x\n", fastRefreshCount,
frameFlags);
#else
// If we do want to use FULL for BACKGROUND updates
refresh = FULL;
reason = FLAGGED_BACKGROUND;
LOG_DEBUG("refresh=FULL, reason=FLAGGED_BACKGROUND\n");
#endif
}
// Sanity: confirm that we did ask for a RESPONSIVE frame.
if (frameFlags & RESPONSIVE) {
refresh = FAST;
reason = NO_OBJECTIONS;
LOG_DEBUG("refresh=FAST, reason=NO_OBJECTIONS, fastRefreshCount=%lu, frameFlags=0x%x\n", fastRefreshCount, frameFlags);
}
}
// Reset the timer used for rate-limiting
void EInkDynamicDisplay::resetRateLimiting()
{
previousRunMs = millis();
}
// Generate a hash of this frame, to compare against previous update
void EInkDynamicDisplay::hashImage()
{
imageHash = 0;
// Sum all bytes of the image buffer together
for (uint16_t b = 0; b < (displayWidth / 8) * displayHeight; b++) {
imageHash += buffer[b];
}
}
// Store the results of determineMode() for future use, and reset for next call
void EInkDynamicDisplay::storeAndReset()
{
previousFrameFlags = frameFlags;
previousRefresh = refresh;
previousReason = reason;
// Only store image hash if the display will update
if (refresh != SKIPPED) {
previousImageHash = imageHash;
}
frameFlags = BACKGROUND;
refresh = UNSPECIFIED;
}
#ifdef EINK_LIMIT_GHOSTING_PX
// Count how many ghost pixels the new image will display
void EInkDynamicDisplay::countGhostPixels()
{
// If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED)
return;
// Start a new count
ghostPixelCount = 0;
// Check new image, bit by bit, for any white pixels at locations marked "dirty"
for (uint16_t i = 0; i < displayBufferSize; i++) {
for (uint8_t bit = 0; bit < 7; bit++) {
const bool dirty = (dirtyPixels[i] >> bit) & 1; // Has pixel location been drawn to since full-refresh?
const bool shouldBeBlank = !((buffer[i] >> bit) & 1); // Is pixel location white in the new image?
// If pixel is (or has been) black since last full-refresh, and now is white: ghosting
if (dirty && shouldBeBlank)
ghostPixelCount++;
// Update the dirty status for this pixel - will this location become a ghost if set white in future?
if (!dirty && !shouldBeBlank)
dirtyPixels[i] |= (1 << bit);
}
}
LOG_DEBUG("ghostPixels=%hu, ", ghostPixelCount);
}
// Check if ghost pixel count exceeds the defined limit
void EInkDynamicDisplay::checkExcessiveGhosting()
{
// If a decision was already reached, don't run the check
if (refresh != UNSPECIFIED)
return;
countGhostPixels();
// If too many ghost pixels, select full refresh
if (ghostPixelCount > EINK_LIMIT_GHOSTING_PX) {
refresh = FULL;
reason = EXCEEDED_GHOSTINGLIMIT;
LOG_DEBUG("refresh=FULL, reason=EXCEEDED_GHOSTINGLIMIT, frameFlags=0x%x\n", frameFlags);
}
}
// Clear the dirty pixels array. Call when full-refresh cleans the display.
void EInkDynamicDisplay::resetGhostPixelTracking()
{
// Copy the current frame into dirtyPixels[] from the display buffer
memcpy(dirtyPixels, EInkDisplay::buffer, EInkDisplay::displayBufferSize);
}
#endif // EINK_LIMIT_GHOSTING_PX
// Handle any asyc tasks
void EInkDynamicDisplay::onNotify(uint32_t notification)
{
// Which task
switch (notification) {
case DUE_POLL_ASYNCREFRESH:
pollAsyncRefresh();
break;
}
}
#ifdef HAS_EINK_ASYNCFULL
// Run the post-update code if the hardware is ready
void EInkDynamicDisplay::pollAsyncRefresh()
{
// We shouldn't be here..
if (!asyncRefreshRunning)
return;
// Still running, check back later
if (adafruitDisplay->epd2.isBusy()) {
// Schedule next call of pollAsyncRefresh()
NotifiedWorkerThread::notifyLater(intervalPollAsyncRefresh, DUE_POLL_ASYNCREFRESH, true);
return;
}
// If asyncRefreshRunning flag is still set, but display's BUSY pin reports the refresh is done
adafruitDisplay->endAsyncFull(); // Run the end of nextPage() code
EInkDisplay::endUpdate(); // Run base-class code to finish off update (NOT our derived class override)
asyncRefreshRunning = false; // Unset the flag
LOG_DEBUG("Async full-refresh complete\n");
// Note: this code only works because of a modification to meshtastic/GxEPD2.
// It is only equipped to intercept calls to nextPage()
}
// Check the status of "async full-refresh"; skip if running
void EInkDynamicDisplay::checkBusyAsyncRefresh()
{
// No refresh taking place, continue with determineMode()
if (!asyncRefreshRunning)
return;
// Full refresh still running
if (adafruitDisplay->epd2.isBusy()) {
// No refresh
refresh = SKIPPED;
// Set the reason, marking what type of frame we're skipping
if (frameFlags & DEMAND_FAST)
reason = ASYNC_REFRESH_BLOCKED_DEMANDFAST;
else if (frameFlags & COSMETIC)
reason = ASYNC_REFRESH_BLOCKED_COSMETIC;
else if (frameFlags & RESPONSIVE)
reason = ASYNC_REFRESH_BLOCKED_RESPONSIVE;
else
reason = ASYNC_REFRESH_BLOCKED_BACKGROUND;
return;
}
}
// Hold control while an async refresh runs
void EInkDynamicDisplay::awaitRefresh()
{
// Continually poll the BUSY pin
while (adafruitDisplay->epd2.isBusy())
yield();
// End the full-refresh process
adafruitDisplay->endAsyncFull(); // Run the end of nextPage() code
EInkDisplay::endUpdate(); // Run base-class code to finish off update (NOT our derived class override)
asyncRefreshRunning = false; // Unset the flag
}
#endif // HAS_EINK_ASYNCFULL
#endif // USE_EINK_DYNAMICDISPLAY
+138
View File
@@ -0,0 +1,138 @@
#pragma once
#include "configuration.h"
#if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY)
#include "EInkDisplay2.h"
#include "GxEPD2_BW.h"
#include "concurrency/NotifiedWorkerThread.h"
/*
Derives from the EInkDisplay adapter class.
Accepts suggestions from Screen class about frame type.
Determines which refresh type is most suitable.
(Full, Fast, Skip)
*/
class EInkDynamicDisplay : public EInkDisplay, protected concurrency::NotifiedWorkerThread
{
public:
// Constructor
// ( Parameters unused, passed to EInkDisplay. Maintains compatibility OLEDDisplay class )
EInkDynamicDisplay(uint8_t address, int sda, int scl, OLEDDISPLAY_GEOMETRY geometry, HW_I2C i2cBus);
~EInkDynamicDisplay();
// What kind of frame is this
enum frameFlagTypes : uint8_t {
BACKGROUND = (1 << 0), // For frames via display()
RESPONSIVE = (1 << 1), // For frames via forceDisplay()
COSMETIC = (1 << 2), // For splashes
DEMAND_FAST = (1 << 3), // Special case only
BLOCKING = (1 << 4), // Modifier - block while refresh runs
};
void addFrameFlag(frameFlagTypes flag);
// Set the correct frame flag, then call universal "update()" method
void display() override;
bool forceDisplay(uint32_t msecLimit) override; // Shadows base class. Parameter and return val unused.
protected:
enum refreshTypes : uint8_t { // Which refresh operation will be used
UNSPECIFIED,
FULL,
FAST,
SKIPPED,
};
enum reasonTypes : uint8_t { // How was the decision reached
NO_OBJECTIONS,
ASYNC_REFRESH_BLOCKED_DEMANDFAST,
ASYNC_REFRESH_BLOCKED_COSMETIC,
ASYNC_REFRESH_BLOCKED_RESPONSIVE,
ASYNC_REFRESH_BLOCKED_BACKGROUND,
EXCEEDED_RATELIMIT_FAST,
EXCEEDED_RATELIMIT_FULL,
FLAGGED_COSMETIC,
FLAGGED_DEMAND_FAST,
EXCEEDED_LIMIT_FASTREFRESH,
EXCEEDED_GHOSTINGLIMIT,
FRAME_MATCHED_PREVIOUS,
BACKGROUND_USES_FAST,
FLAGGED_BACKGROUND,
REDRAW_WITH_FULL,
};
enum notificationTypes : uint8_t { // What was onNotify() called for
NONE = 0, // This behavior (NONE=0) is fixed by NotifiedWorkerThread class
DUE_POLL_ASYNCREFRESH = 1,
};
const uint32_t intervalPollAsyncRefresh = 100;
void onNotify(uint32_t notification) override; // Handle any async tasks - overrides NotifiedWorkerThread
void configForFastRefresh(); // GxEPD2 code to set fast-refresh
void configForFullRefresh(); // GxEPD2 code to set full-refresh
bool determineMode(); // Assess situation, pick a refresh type
void applyRefreshMode(); // Run any relevant GxEPD2 code, so next update will use correct refresh type
void adjustRefreshCounters(); // Update fastRefreshCount
bool update(); // Trigger the display update - determine mode, then call base class
void endOrDetach(); // Run the post-update code, or delegate it off to checkBusyAsyncRefresh()
// Checks as part of determineMode()
void checkInitialized(); // Is this the very first frame?
void checkForPromotion(); // Was a frame skipped (rate, display busy) that should have been a FAST refresh?
void checkRateLimiting(); // Is this frame too soon?
void checkCosmetic(); // Was the COSMETIC flag set?
void checkDemandingFast(); // Was the DEMAND_FAST flag set?
void checkFrameMatchesPrevious(); // Does the new frame match the existing display image?
void checkConsecutiveFastRefreshes(); // Too many fast-refreshes consecutively?
void checkFastRequested(); // Was the flag set for RESPONSIVE, or only BACKGROUND?
void resetRateLimiting(); // Set previousRunMs - this now counts as an update, for rate-limiting
void hashImage(); // Generate a hashed version of this frame, to compare against previous update
void storeAndReset(); // Keep results of determineMode() for later, tidy-up for next call
// What we are determining for this frame
frameFlagTypes frameFlags = BACKGROUND; // Frame characteristics - determineMode() input
refreshTypes refresh = UNSPECIFIED; // Refresh type - determineMode() output
reasonTypes reason = NO_OBJECTIONS; // Reason - why was refresh type used
// What happened last time determineMode() ran
frameFlagTypes previousFrameFlags = BACKGROUND; // (Previous) Frame flags
refreshTypes previousRefresh = UNSPECIFIED; // (Previous) Outcome
reasonTypes previousReason = NO_OBJECTIONS; // (Previous) Reason
bool initialized = false; // Have we drawn at least one frame yet?
uint32_t previousRunMs = -1; // When did determineMode() last run (rather than rejecting for rate-limiting)
uint32_t imageHash = 0; // Hash of the current frame. Don't bother updating if nothing has changed!
uint32_t previousImageHash = 0; // Hash of the previous update's frame
uint32_t fastRefreshCount = 0; // How many fast-refreshes consecutively since last full refresh?
refreshTypes currentConfig = FULL; // Which refresh type is GxEPD2 currently configured for
// Optional - track ghosting, pixel by pixel
#ifdef EINK_LIMIT_GHOSTING_PX
void countGhostPixels(); // Count any pixels which have moved from black to white since last full-refresh
void checkExcessiveGhosting(); // Check if ghosting exceeds defined limit
void resetGhostPixelTracking(); // Clear the dirty pixels array. Call when full-refresh cleans the display.
uint8_t *dirtyPixels; // Any pixels that have been black since last full-refresh (dynamically allocated mem)
uint32_t ghostPixelCount = 0; // Number of pixels with problematic ghosting. Retained here for LOG_DEBUG use
#endif
// Conditional - async full refresh - only with modified meshtastic/GxEPD2
#if defined(HAS_EINK_ASYNCFULL)
void pollAsyncRefresh(); // Run the post-update code if the hardware is ready
void checkBusyAsyncRefresh(); // Check if display is busy running an async full-refresh (rejecting new frames)
void awaitRefresh(); // Hold control while an async refresh runs
void endUpdate() override {} // Disable base-class behavior of running post-update immediately after forceDisplay()
bool asyncRefreshRunning = false; // Flag, checked by checkBusyAsyncRefresh()
#else
void pollAsyncRefresh() {} // Dummy method. In theory, not reachable
#endif
};
// Tidier calls to addFrameFlag() from outside class
#define EINK_ADD_FRAMEFLAG(display, flag) static_cast<EInkDynamicDisplay *>(display)->addFrameFlag(EInkDynamicDisplay::flag)
#else // !USE_EINK_DYNAMICDISPLAY
// Dummy-macro, removes the need for include guards
#define EINK_ADD_FRAMEFLAG(display, flag)
#endif
+162 -57
View File
@@ -25,12 +25,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <OLEDDisplay.h>
#include "DisplayFormatters.h"
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h"
#endif
#include "MeshService.h"
#include "NodeDB.h"
#include "error.h"
#include "gps/GeoCoord.h"
#include "gps/RTC.h"
#include "graphics/ScreenFonts.h"
#include "graphics/images.h"
#include "input/TouchScreenImpl1.h"
#include "main.h"
@@ -56,14 +59,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "platform/portduino/PortduinoGlue.h"
#endif
#ifdef OLED_RU
#include "fonts/OLEDDisplayFontsRU.h"
#endif
#ifdef OLED_UA
#include "fonts/OLEDDisplayFontsUA.h"
#endif
using namespace meshtastic; /** @todo remove */
namespace graphics
@@ -78,7 +73,7 @@ namespace graphics
// #define SHOW_REDRAWS
// A text message frame + debug frame + all the node infos
static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
FrameCallback *normalFrames;
static uint32_t targetFramerate = IDLE_FRAMERATE;
static char btPIN[16] = "888888";
@@ -99,8 +94,10 @@ std::vector<MeshModule *> moduleFrames;
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
static char ourId[5];
#if HAS_GPS
// GeoCoord object for the screen
GeoCoord geoCoord;
#endif
#ifdef SHOW_REDRAWS
static bool heartbeat = false;
@@ -111,31 +108,7 @@ static uint16_t displayWidth, displayHeight;
#define SCREEN_WIDTH displayWidth
#define SCREEN_HEIGHT displayHeight
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
// The screen is bigger so use bigger fonts
#define FONT_SMALL ArialMT_Plain_16 // Height: 19
#define FONT_MEDIUM ArialMT_Plain_24 // Height: 28
#define FONT_LARGE ArialMT_Plain_24 // Height: 28
#else
#ifdef OLED_RU
#define FONT_SMALL ArialMT_Plain_10_RU
#else
#ifdef OLED_UA
#define FONT_SMALL ArialMT_Plain_10_UA
#else
#define FONT_SMALL ArialMT_Plain_10 // Height: 13
#endif
#endif
#define FONT_MEDIUM ArialMT_Plain_16 // Height: 19
#define FONT_LARGE ArialMT_Plain_24 // Height: 28
#endif
#define fontHeight(font) ((font)[1] + 1) // height is position 1
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE)
#include "graphics/ScreenFonts.h"
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
@@ -289,10 +262,65 @@ static void drawWelcomeScreen(OLEDDisplay *display, OLEDDisplayUiState *state, i
#ifdef USE_EINK
/// Used on eink displays while in deep sleep
static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
static void drawDeepSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// Next frame should use full-refresh, and block while running, else device will sleep before async callback
EINK_ADD_FRAMEFLAG(display, COSMETIC);
EINK_ADD_FRAMEFLAG(display, BLOCKING);
LOG_DEBUG("Drawing deep sleep screen\n");
drawIconScreen("Sleeping...", display, state, x, y);
}
/// Used on eink displays when screen updates are paused
static void drawScreensaverOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
{
LOG_DEBUG("Drawing screensaver overlay\n");
EINK_ADD_FRAMEFLAG(display, COSMETIC); // Take the opportunity for a full-refresh
// Config
display->setFont(FONT_SMALL);
display->setTextAlignment(TEXT_ALIGN_LEFT);
const char *pauseText = "Screen Paused";
const char *idText = owner.short_name;
constexpr uint16_t padding = 5;
constexpr uint8_t dividerGap = 1;
constexpr uint8_t imprecision = 5; // How far the box origins can drift from center. Combat burn-in.
// Dimensions
const uint16_t idTextWidth = display->getStringWidth(idText, strlen(idText));
const uint16_t pauseTextWidth = display->getStringWidth(pauseText, strlen(pauseText));
const uint16_t boxWidth = padding + idTextWidth + padding + padding + pauseTextWidth + padding;
const uint16_t boxHeight = padding + FONT_HEIGHT_SMALL + padding;
// Position
const int16_t boxLeft = (display->width() / 2) - (boxWidth / 2) + random(-imprecision, imprecision + 1);
// const int16_t boxRight = boxLeft + boxWidth - 1;
const int16_t boxTop = (display->height() / 2) - (boxHeight / 2 + random(-imprecision, imprecision + 1));
const int16_t boxBottom = boxTop + boxHeight - 1;
const int16_t idTextLeft = boxLeft + padding;
const int16_t idTextTop = boxTop + padding;
const int16_t pauseTextLeft = boxLeft + padding + idTextWidth + padding + padding;
const int16_t pauseTextTop = boxTop + padding;
const int16_t dividerX = boxLeft + padding + idTextWidth + padding;
const int16_t dividerTop = boxTop + 1 + dividerGap;
const int16_t dividerBottom = boxBottom - 1 - dividerGap;
// Draw: box
display->setColor(EINK_WHITE);
display->fillRect(boxLeft - 1, boxTop - 1, boxWidth + 2, boxHeight + 2); // Clear a slightly oversized area for the box
display->setColor(EINK_BLACK);
display->drawRect(boxLeft, boxTop, boxWidth, boxHeight);
// Draw: Text
display->drawString(idTextLeft, idTextTop, idText);
display->drawString(pauseTextLeft, pauseTextTop, pauseText);
display->drawString(pauseTextLeft + 1, pauseTextTop, pauseText); // Faux bold
// Draw: divider
display->drawLine(dividerX, dividerTop, dividerX, dividerBottom);
}
#endif
static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
@@ -381,7 +409,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
static char tempBuf[237];
const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(getFrom(&mp));
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
// LOG_DEBUG("drawing text message from 0x%x: %s\n", mp.from,
// mp.decoded.variant.data.decoded.bytes);
@@ -419,7 +447,7 @@ static void drawWaypointFrame(OLEDDisplay *display, OLEDDisplayUiState *state, i
static char tempBuf[237];
meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(getFrom(&mp));
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
@@ -510,7 +538,7 @@ static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const NodeStat
if (config.display.heading_bold)
display->drawString(x + 11, y - 2, usersString);
}
#if HAS_GPS
// Draw GPS status summary
static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus *gps)
{
@@ -652,7 +680,7 @@ static void drawGPScoordinates(OLEDDisplay *display, int16_t x, int16_t y, const
}
}
}
#endif
namespace
{
@@ -807,16 +835,16 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
if (state->currentFrame != prevFrame) {
prevFrame = state->currentFrame;
nodeIndex = (nodeIndex + 1) % nodeDB.getNumMeshNodes();
meshtastic_NodeInfoLite *n = nodeDB.getMeshNodeByIndex(nodeIndex);
if (n->num == nodeDB.getNodeNum()) {
nodeIndex = (nodeIndex + 1) % nodeDB->getNumMeshNodes();
meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(nodeIndex);
if (n->num == nodeDB->getNodeNum()) {
// Don't show our node, just skip to next
nodeIndex = (nodeIndex + 1) % nodeDB.getNumMeshNodes();
n = nodeDB.getMeshNodeByIndex(nodeIndex);
nodeIndex = (nodeIndex + 1) % nodeDB->getNumMeshNodes();
n = nodeDB->getMeshNodeByIndex(nodeIndex);
}
}
meshtastic_NodeInfoLite *node = nodeDB.getMeshNodeByIndex(nodeIndex);
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(nodeIndex);
display->setFont(FONT_SMALL);
@@ -854,7 +882,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
} else {
strncpy(distStr, "? km", sizeof(distStr));
}
meshtastic_NodeInfoLite *ourNode = nodeDB.getMeshNode(nodeDB.getNodeNum());
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
int16_t compassX = 0, compassY = 0;
@@ -920,6 +948,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry)
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32)
{
graphics::normalFrames = new FrameCallback[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
dispdev = new SH1106Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
@@ -929,9 +958,12 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(RAK14014)
dispdev = new TFTDisplay(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_EINK)
#elif defined(USE_EINK) && !defined(USE_EINK_DYNAMICDISPLAY)
dispdev = new EInkDisplay(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY)
dispdev = new EInkDynamicDisplay(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_ST7567)
dispdev = new ST7567Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
@@ -955,6 +987,11 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
cmdQueue.setReader(this);
}
Screen::~Screen()
{
delete[] graphics::normalFrames;
}
/**
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
@@ -962,15 +999,17 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
void Screen::doDeepSleep()
{
#ifdef USE_EINK
static FrameCallback sleepFrames[] = {drawSleepScreen};
static const int sleepFrameCount = sizeof(sleepFrames) / sizeof(sleepFrames[0]);
ui->setFrames(sleepFrames, sleepFrameCount);
ui->update();
setOn(false, drawDeepSleepScreen);
#ifdef PIN_EINK_EN
digitalWrite(PIN_EINK_EN, LOW); // power off backlight
#endif
#else
// Without E-Ink display:
setOn(false);
#endif
}
void Screen::handleSetOn(bool on)
void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
{
if (!useDisplay)
return;
@@ -989,6 +1028,10 @@ void Screen::handleSetOn(bool on)
setInterval(0); // Draw ASAP
runASAP = true;
} else {
#ifdef USE_EINK
// eInkScreensaver parameter is usually NULL (default argument), default frame used instead
setScreensaverFrames(einkScreensaver);
#endif
LOG_INFO("Turning off screen\n");
dispdev->displayOff();
#ifdef T_WATCH_S3
@@ -1039,6 +1082,7 @@ void Screen::setup()
logo_timeout *= 2;
// Add frames.
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST);
static FrameCallback bootFrames[] = {drawBootScreen};
static const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
ui->setFrames(bootFrames, bootFrameCount);
@@ -1195,6 +1239,7 @@ int32_t Screen::runOnce()
break;
case Cmd::STOP_BLUETOOTH_PIN_SCREEN:
case Cmd::STOP_BOOT_SCREEN:
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame
setFrames();
break;
case Cmd::PRINT:
@@ -1293,6 +1338,58 @@ void Screen::setWelcomeFrames()
}
}
#ifdef USE_EINK
/// Determine which screensaver frame to use, then set the FrameCallback
void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
{
// Remember current frame, restore position at power-on
uint8_t frameNumber = ui->getUiState()->currentFrame;
// Retain specified frame / overlay callback beyond scope of this method
static FrameCallback screensaverFrame;
static OverlayCallback screensaverOverlay;
// If: one-off screensaver frame passed as argument. Handles doDeepSleep()
if (einkScreensaver != NULL) {
screensaverFrame = einkScreensaver;
ui->setFrames(&screensaverFrame, 1);
}
// Else, display the usual "overlay" screensaver
else {
screensaverOverlay = drawScreensaverOverlay;
ui->setOverlays(&screensaverOverlay, 1);
}
// Request new frame, ASAP
setFastFramerate();
uint64_t startUpdate;
do {
startUpdate = millis(); // Handle impossibly unlikely corner case of a millis() overflow..
delay(1);
ui->update();
} while (ui->getUiState()->lastUpdate < startUpdate);
#ifndef USE_EINK_DYNAMICDISPLAY
// Retrofit to EInkDisplay class
delay(10);
screen->forceDisplay();
#endif
// Prepare now for next frame, shown when display wakes
ui->setOverlays(NULL, 0); // Clear overlay
setFrames(); // Return to normal display updates
ui->switchToFrame(frameNumber); // Attempt to return to same frame after power-on
// Pick a refresh method, for when display wakes
#ifdef EINK_HASQUIRK_GHOSTING
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // Really ugly to see ghosting from "screen paused"
#else
EINK_ADD_FRAMEFLAG(dispdev, RESPONSIVE); // Really nice to wake screen with a fast-refresh
#endif
}
#endif
// restore our regular frame list
void Screen::setFrames()
{
@@ -1307,7 +1404,7 @@ void Screen::setFrames()
#endif
// We don't show the node info our our node (if we have it yet - we should)
size_t numMeshNodes = nodeDB.getNumMeshNodes();
size_t numMeshNodes = nodeDB->getNumMeshNodes();
if (numMeshNodes > 0)
numMeshNodes--;
@@ -1375,6 +1472,7 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
{
LOG_DEBUG("showing bluetooth screen\n");
showingNormalScreen = false;
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
static FrameCallback frames[] = {drawFrameBluetooth};
snprintf(btPIN, sizeof(btPIN), "%06u", pin);
@@ -1392,6 +1490,8 @@ void Screen::handleShutdownScreen()
{
LOG_DEBUG("showing shutdown screen\n");
showingNormalScreen = false;
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Use fast-refresh for next frame, no skip please
EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
drawFrameText(display, state, x, y, "Shutting down...");
@@ -1399,12 +1499,14 @@ void Screen::handleShutdownScreen()
static FrameCallback frames[] = {frame};
setFrameImmediateDraw(frames);
forceDisplay();
}
void Screen::handleRebootScreen()
{
LOG_DEBUG("showing reboot screen\n");
showingNormalScreen = false;
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
drawFrameText(display, state, x, y, "Rebooting...");
@@ -1417,6 +1519,7 @@ void Screen::handleStartFirmwareUpdateScreen()
{
LOG_DEBUG("showing firmware screen\n");
showingNormalScreen = false;
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
static FrameCallback frames[] = {drawFrameFirmware};
setFrameImmediateDraw(frames);
@@ -1529,8 +1632,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
char channelStr[20];
{
concurrency::LockGuard guard(&lock);
auto chName = channels.getPrimaryName();
snprintf(channelStr, sizeof(channelStr), "%s", chName);
snprintf(channelStr, sizeof(channelStr), "#%s", channels.getName(channels.getPrimaryIndex()));
}
// Display power status
@@ -1553,6 +1655,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
} else {
drawNodes(display, x + (SCREEN_WIDTH * 0.25), y + 3, nodeStatus);
}
#if HAS_GPS
// Display GPS status
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
drawGPSpowerstat(display, x, y + 2, gpsStatus);
@@ -1563,7 +1666,7 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 3, gpsStatus);
}
}
#endif
display->setColor(WHITE);
// Draw the channel name
display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr);
@@ -1782,6 +1885,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
char chUtil[13];
snprintf(chUtil, sizeof(chUtil), "ChUtil %2.0f%%", airTime->channelUtilizationPercent());
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil), y + FONT_HEIGHT_SMALL * 1, chUtil);
#if HAS_GPS
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
// Line 3
if (config.display.gps_format !=
@@ -1793,6 +1897,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
} else {
drawGPSpowerstat(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
}
#endif
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
#ifdef SHOW_REDRAWS
if (heartbeat)
@@ -1809,7 +1914,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
setFrames(); // Regen the list of screens
}
nodeDB.updateGUI = false;
nodeDB->updateGUI = false;
break;
}
+17 -5
View File
@@ -47,6 +47,7 @@ class Screen
#endif
#include "EInkDisplay2.h"
#include "EInkDynamicDisplay.h"
#include "TFTDisplay.h"
#include "TypedQueue.h"
#include "commands.h"
@@ -72,6 +73,10 @@ class Screen
#define MILES_TO_FEET 5280
#endif
// Intuitive colors. E-Ink display is inverted from OLED(?)
#define EINK_BLACK OLEDDISPLAY_COLOR::WHITE
#define EINK_WHITE OLEDDISPLAY_COLOR::BLACK
namespace graphics
{
@@ -124,6 +129,8 @@ class Screen : public concurrency::OSThread
public:
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
~Screen();
Screen(const Screen &) = delete;
Screen &operator=(const Screen &) = delete;
@@ -136,12 +143,12 @@ class Screen : public concurrency::OSThread
// Not thread safe - must be called before any other methods are called.
void setup();
/// Turns the screen on/off.
void setOn(bool on)
/// Turns the screen on/off. Optionally, pass a custom screensaver frame for E-Ink
void setOn(bool on, FrameCallback einkScreensaver = NULL)
{
if (!on)
handleSetOn(
false); // We handle off commands immediately, because they might be called because the CPU is shutting down
// We handle off commands immediately, because they might be called because the CPU is shutting down
handleSetOn(false, einkScreensaver);
else
enqueueCmd(ScreenCmd{.cmd = on ? Cmd::SET_ON : Cmd::SET_OFF});
}
@@ -318,6 +325,11 @@ class Screen : public concurrency::OSThread
void setWelcomeFrames();
#ifdef USE_EINK
/// Draw an image to remain on E-Ink display after screen off
void setScreensaverFrames(FrameCallback einkScreensaver = NULL);
#endif
protected:
/// Updates the UI.
//
@@ -348,7 +360,7 @@ class Screen : public concurrency::OSThread
}
// Implementations of various commands, called from doTask().
void handleSetOn(bool on);
void handleSetOn(bool on, FrameCallback einkScreensaver = NULL);
void handleOnPress();
void handleShowNextFrame();
void handleShowPrevFrame();
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#ifdef OLED_RU
#include "graphics/fonts/OLEDDisplayFontsRU.h"
#endif
#ifdef OLED_UA
#include "graphics/fonts/OLEDDisplayFontsUA.h"
#endif
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
// The screen is bigger so use bigger fonts
#define FONT_SMALL ArialMT_Plain_16 // Height: 19
#define FONT_MEDIUM ArialMT_Plain_24 // Height: 28
#define FONT_LARGE ArialMT_Plain_24 // Height: 28
#else
#ifdef OLED_RU
#define FONT_SMALL ArialMT_Plain_10_RU
#else
#ifdef OLED_UA
#define FONT_SMALL ArialMT_Plain_10_UA
#else
#define FONT_SMALL ArialMT_Plain_10 // Height: 13
#endif
#endif
#define FONT_MEDIUM ArialMT_Plain_16 // Height: 19
#define FONT_LARGE ArialMT_Plain_24 // Height: 28
#endif
#define fontHeight(font) ((font)[1] + 1) // height is position 1
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE)
+2 -1
View File
@@ -24,7 +24,8 @@ LinuxInput::LinuxInput(const char *name) : concurrency::OSThread(name)
void LinuxInput::deInit()
{
close(fd);
if (fd >= 0)
close(fd);
}
int32_t LinuxInput::runOnce()
+1 -1
View File
@@ -38,7 +38,7 @@ class LinuxInput : public Observable<const InputEvent *>, public concurrency::OS
int queue_progress = 0;
struct epoll_event events[MAX_EVENTS];
int fd;
int fd = -1;
int ret;
uint8_t report[8];
int epollfd;
+6
View File
@@ -216,6 +216,12 @@ int32_t KbI2cBase::runOnce()
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
e.kbchar = 0xb7;
break;
case 0x90: // fn+r
case 0x9b: // fn+s
// just pass those unmodified
e.inputEvent = ANYKEY;
e.kbchar = c;
break;
case 0x0d: // Enter
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
break;
+58 -19
View File
@@ -1,4 +1,7 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h"
#endif
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
@@ -6,7 +9,7 @@
#include "ReliableRouter.h"
#include "airtime.h"
#include "buzz.h"
#include "configuration.h"
#include "error.h"
#include "power.h"
// #include "debug.h"
@@ -33,10 +36,14 @@
// #include <driver/rtc_io.h>
#ifdef ARCH_ESP32
#if !MESHTASTIC_EXCLUDE_WEBSERVER
#include "mesh/http/WebServer.h"
#endif
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
#include "nimble/NimbleBluetooth.h"
NimbleBluetooth *nimbleBluetooth;
#endif
#endif
#ifdef ARCH_NRF52
#include "NRF52Bluetooth.h"
@@ -52,22 +59,28 @@ NRF52Bluetooth *nrf52Bluetooth;
#include "mesh/api/ethServerAPI.h"
#include "mesh/eth/ethClient.h"
#endif
#if !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h"
#endif
#include "LLCC68Interface.h"
#include "RF95Interface.h"
#include "SX1262Interface.h"
#include "SX1268Interface.h"
#include "SX1280Interface.h"
#ifdef ARCH_STM32WL
#include "STM32WLE5JCInterface.h"
#endif
#if !HAS_RADIO && defined(ARCH_PORTDUINO)
#include "platform/portduino/SimRadio.h"
#endif
#ifdef ARCH_PORTDUINO
#include "linux/LinuxHardwareI2C.h"
#include "mesh/raspihttp/PiWebServer.h"
#include "platform/portduino/PortduinoGlue.h"
#include <fstream>
#include <iostream>
@@ -77,6 +90,7 @@ NRF52Bluetooth *nrf52Bluetooth;
#if HAS_BUTTON || defined(ARCH_PORTDUINO)
#include "ButtonThread.h"
#endif
#include "PowerFSMThread.h"
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
@@ -218,10 +232,16 @@ void setup()
initDeepSleep();
// Testing this fix für erratic T-Echo boot behaviour
#if defined(TTGO_T_ECHO) && defined(PIN_EINK_PWR_ON)
pinMode(PIN_EINK_PWR_ON, OUTPUT);
digitalWrite(PIN_EINK_PWR_ON, HIGH);
// power on peripherals
#if defined(TTGO_T_ECHO) && defined(PIN_POWER_EN)
pinMode(PIN_POWER_EN, OUTPUT);
digitalWrite(PIN_POWER_EN, HIGH);
// digitalWrite(PIN_POWER_EN1, INPUT);
#endif
#if defined(LORA_TCXO_GPIO)
pinMode(LORA_TCXO_GPIO, OUTPUT);
digitalWrite(LORA_TCXO_GPIO, HIGH);
#endif
#if defined(VEXT_ENABLE_V03)
@@ -340,7 +360,7 @@ void setup()
pinMode(PIN_3V3_EN, OUTPUT);
digitalWrite(PIN_3V3_EN, HIGH);
#endif
#ifndef USE_EINK
#ifdef AQ_SET_PIN
// RAK-12039 set pin for Air quality sensor
pinMode(AQ_SET_PIN, OUTPUT);
digitalWrite(AQ_SET_PIN, HIGH);
@@ -499,6 +519,7 @@ void setup()
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BME_680, meshtastic_TelemetrySensorType_BME680)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BME_280, meshtastic_TelemetrySensorType_BME280)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_280, meshtastic_TelemetrySensorType_BMP280)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::BMP_085, meshtastic_TelemetrySensorType_BMP085)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA260, meshtastic_TelemetrySensorType_INA260)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA219, meshtastic_TelemetrySensorType_INA219)
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::INA3221, meshtastic_TelemetrySensorType_INA3221)
@@ -546,7 +567,7 @@ void setup()
// We do this as early as possible because this loads preferences from flash
// but we need to do this after main cpu init (esp32setup), because we need the random seed set
nodeDB.init();
nodeDB = new NodeDB;
// If we're taking on the repeater role, use flood router and turn off 3V3_S rail because peripherals are not needed
if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
@@ -621,7 +642,7 @@ void setup()
#else
// ESP32
SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
LOG_WARN("SPI.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)\n", LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
LOG_DEBUG("SPI.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)\n", LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS);
SPI.setFrequency(4000000);
#endif
@@ -630,17 +651,22 @@ void setup()
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
#if !MESHTASTIC_EXCLUDE_GPS
// If we're taking on the repeater role, ignore GPS
if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) {
gps = GPS::createGps();
if (HAS_GPS) {
if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) {
gps = GPS::createGps();
if (gps) {
gpsStatus->observe(&gps->newStatus);
} else {
LOG_DEBUG("Running without GPS.\n");
}
}
}
if (gps) {
gpsStatus->observe(&gps->newStatus);
} else {
LOG_DEBUG("Running without GPS.\n");
}
nodeStatus->observe(&nodeDB.newStatus);
#endif
nodeStatus->observe(&nodeDB->newStatus);
#ifdef HAS_I2S
LOG_DEBUG("Starting audio thread\n");
@@ -679,6 +705,11 @@ void setup()
digitalWrite(SX126X_ANT_SW, 1);
#endif
#ifdef PIN_PWR_DELAY_MS
// This may be required to give the peripherals time to power up.
delay(PIN_PWR_DELAY_MS);
#endif
#ifdef ARCH_PORTDUINO
if (settingsMap[use_sx1262]) {
if (!rIf) {
@@ -829,7 +860,7 @@ void setup()
if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())) {
LOG_WARN("Radio chip does not support 2.4GHz LoRa. Reverting to unset.\n");
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
nodeDB.saveToDisk(SEGMENT_CONFIG);
nodeDB->saveToDisk(SEGMENT_CONFIG);
if (!rIf->reconfigure()) {
LOG_WARN("Reconfigure failed, rebooting\n");
screen->startRebootScreen();
@@ -837,9 +868,12 @@ void setup()
}
}
#if !MESHTASTIC_EXCLUDE_MQTT
mqttInit();
#endif
#ifndef ARCH_PORTDUINO
// Initialize Wifi
#if HAS_WIFI
initWifi();
@@ -851,12 +885,17 @@ void setup()
#endif
#endif
#ifdef ARCH_ESP32
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WEBSERVER
// Start web server thread.
webServerThread = new WebServerThread();
#endif
#ifdef ARCH_PORTDUINO
#if __has_include(<ulfius.h>)
if (settingsMap[webserverport] != -1) {
piwebServerThread = new PiWebServerThread();
}
#endif
initApiServer(TCPPort);
#endif
+1
View File
@@ -54,6 +54,7 @@ extern int TCPPort; // set by Portduino
// Global Screen singleton.
extern graphics::Screen *screen;
// extern Observable<meshtastic::PowerStatus> newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class
// extern meshtastic::PowerStatus *powerStatus;
+39 -31
View File
@@ -2,10 +2,15 @@
#include "CryptoEngine.h"
#include "DisplayFormatters.h"
#include "NodeDB.h"
#include "RadioInterface.h"
#include "configuration.h"
#include <assert.h>
#if !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h"
#endif
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01};
@@ -15,7 +20,9 @@ Channels channels;
const char *Channels::adminChannel = "admin";
const char *Channels::gpioChannel = "gpio";
const char *Channels::serialChannel = "serial";
#if !MESHTASTIC_EXCLUDE_MQTT
const char *Channels::mqttChannel = "mqtt";
#endif
uint8_t xorHash(const uint8_t *p, size_t len)
{
@@ -192,6 +199,12 @@ void Channels::onConfigChanged()
if (ch.role == meshtastic_Channel_Role_PRIMARY)
primaryIndex = i;
}
#if !MESHTASTIC_EXCLUDE_MQTT
if (channels.anyMqttEnabled() && mqtt && !mqtt->isEnabled()) {
LOG_DEBUG("MQTT is enabled on at least one channel, so set MQTT thread to run immediately\n");
mqtt->start();
}
#endif
}
meshtastic_Channel &Channels::getByIndex(ChannelIndex chIndex)
@@ -236,6 +249,16 @@ void Channels::setChannel(const meshtastic_Channel &c)
old = c; // slam in the new settings/role
}
bool Channels::anyMqttEnabled()
{
for (int i = 0; i < getNumChannels(); i++)
if (channelFile.channels[i].role != meshtastic_Channel_Role_DISABLED && channelFile.channels[i].has_settings &&
(channelFile.channels[i].settings.downlink_enabled || channelFile.channels[i].settings.uplink_enabled))
return true;
return false;
}
const char *Channels::getName(size_t chIndex)
{
// Convert the short "" representation for Default into a usable string
@@ -254,38 +277,23 @@ const char *Channels::getName(size_t chIndex)
return channelName;
}
/**
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs.
* The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they
their nodes
* aren't talking to each other.
*
* This string is of the form "#name-X".
*
* Where X is either:
* (for custom PSKS) a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together,
*
* This function will also need to be implemented in GUI apps that talk to the radio.
*
* https://github.com/meshtastic/firmware/issues/269
*/
const char *Channels::getPrimaryName()
bool Channels::hasDefaultChannel()
{
static char buf[32];
char suffix;
// auto channelSettings = getPrimary();
// if (channelSettings.psk.size != 1) {
// We have a standard PSK, so generate a letter based hash.
uint8_t code = getHash(primaryIndex);
suffix = 'A' + (code % 26);
/* } else {
suffix = '0' + channelSettings.psk.bytes[0];
} */
snprintf(buf, sizeof(buf), "#%s-%c", getName(primaryIndex), suffix);
return buf;
// If we don't use a preset or the default frequency slot, or we override the frequency, we don't have a default channel
if (!config.lora.use_preset || !RadioInterface::uses_default_frequency_slot || config.lora.override_frequency)
return false;
// Check if any of the channels are using the default name and PSK
for (size_t i = 0; i < getNumChannels(); i++) {
const auto &ch = getByIndex(i);
if (ch.settings.psk.size == 1 && ch.settings.psk.bytes[0] == 1) {
const char *name = getName(i);
const char *presetName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
// Check if the name is the default derived from the modem preset
if (strcmp(name, presetName) == 0)
return true;
}
}
return false;
}
/** Given a channel hash setup crypto for decoding that channel (or the primary channel if that channel is unsecured)
+6 -19
View File
@@ -61,25 +61,6 @@ class Channels
ChannelIndex getNumChannels() { return channelFile.channels_count; }
/**
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different
PSKs.
* The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why
they their nodes
* aren't talking to each other.
*
* This string is of the form "#name-X".
*
* Where X is either:
* (for custom PSKS) a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together,
* OR (for the standard minimially secure PSKs) a number from 0 to 9.
*
* This function will also need to be implemented in GUI apps that talk to the radio.
*
* https://github.com/meshtastic/firmware/issues/269
*/
const char *getPrimaryName();
/// Called by NodeDB on initial boot when the radio config settings are unset. Set a default single channel config.
void initDefaults();
@@ -102,6 +83,12 @@ class Channels
*/
int16_t setActiveByIndex(ChannelIndex channelIndex);
// Returns true if we can be reached via a channel with the default settings given a region and modem preset
bool hasDefaultChannel();
// Returns true if any of our channels have enabled MQTT uplink or downlink
bool anyMqttEnabled();
private:
/** Given a channel index, change to use the crypto key specified by that index
*
+23
View File
@@ -0,0 +1,23 @@
#include "Default.h"
uint32_t Default::getConfiguredOrDefaultMs(uint32_t configuredInterval)
{
if (configuredInterval > 0)
return configuredInterval * 1000;
return default_broadcast_interval_secs * 1000;
}
uint32_t Default::getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval)
{
if (configuredInterval > 0)
return configuredInterval * 1000;
return defaultInterval * 1000;
}
uint32_t Default::getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue)
{
if (configured > 0)
return configured;
return defaultValue;
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <NodeDB.h>
#include <cstdint>
#define ONE_DAY 24 * 60 * 60
#define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60)
#define default_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 15 * 60)
#define default_wait_bluetooth_secs IF_ROUTER(1, 60)
#define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep
#define default_ls_secs IF_ROUTER(ONE_DAY, 5 * 60)
#define default_min_wake_secs 10
#define default_screen_on_secs IF_ROUTER(1, 60 * 10)
#define default_node_info_broadcast_secs 3 * 60 * 60
#define min_node_info_broadcast_secs 60 * 60 // No regular broadcasts of more than once an hour
#define default_mqtt_address "mqtt.meshtastic.org"
#define default_mqtt_username "meshdev"
#define default_mqtt_password "large4cats"
#define default_mqtt_root "msh"
#define IF_ROUTER(routerVal, normalVal) \
((config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER) ? (routerVal) : (normalVal))
class Default
{
public:
static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval);
static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval);
static uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue);
};
-9
View File
@@ -49,15 +49,6 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
tosend->hop_limit--; // bump down the hop count
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
// If it is a traceRoute request, update the route that it went via me
if (traceRouteModule && traceRouteModule->wantPacket(p))
traceRouteModule->updateRoute(tosend);
// If it is a neighborInfo packet, update last_sent_by_id
if (neighborInfoModule && neighborInfoModule->wantPacket(p))
neighborInfoModule->updateLastSentById(tosend);
}
LOG_INFO("Rebroadcasting received floodmsg to neighbors\n");
// Note: we are careful to resend using the original senders node id
// We are careful not to call our hooked version of send() - because we don't want to check this again
-2
View File
@@ -2,8 +2,6 @@
#include "PacketHistory.h"
#include "Router.h"
#include "modules/NeighborInfoModule.h"
#include "modules/TraceRouteModule.h"
/**
* This is a mixin that extends Router with the ability to do Naive Flooding (in the standard mesh protocol sense)
+8 -5
View File
@@ -32,7 +32,8 @@ MeshModule::~MeshModule()
assert(0); // FIXME - remove from list of modules once someone needs this feature
}
meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex,
uint8_t hopStart, uint8_t hopLimit)
{
meshtastic_Routing c = meshtastic_Routing_init_default;
@@ -49,7 +50,7 @@ meshtastic_MeshPacket *MeshModule::allocAckNak(meshtastic_Routing_Error err, Nod
p->priority = meshtastic_MeshPacket_Priority_ACK;
p->hop_limit = config.lora.hop_limit; // Flood ACK back to original sender
p->hop_limit = routingModule->getHopLimitForResponse(hopStart, hopLimit); // Flood ACK back to original sender
p->to = to;
p->decoded.request_id = idFrom;
p->channel = chIndex;
@@ -80,7 +81,7 @@ void MeshModule::callPlugins(meshtastic_MeshPacket &mp, RxSource src)
bool ignoreRequest = false; // No module asked to ignore the request yet
// Was this message directed to us specifically? Will be false if we are sniffing someone elses packets
auto ourNodeNum = nodeDB.getNodeNum();
auto ourNodeNum = nodeDB->getNodeNum();
bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
for (auto i = modules->begin(); i != modules->end(); ++i) {
@@ -176,7 +177,8 @@ void MeshModule::callPlugins(meshtastic_MeshPacket &mp, RxSource src)
// SECURITY NOTE! I considered sending back a different error code if we didn't find the psk (i.e. !isDecoded)
// but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs
// bad.
routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel);
routingModule->sendAckNak(meshtastic_Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel, mp.hop_start,
mp.hop_limit);
}
}
@@ -217,6 +219,7 @@ void setReplyTo(meshtastic_MeshPacket *p, const meshtastic_MeshPacket &to)
assert(p->which_payload_variant == meshtastic_MeshPacket_decoded_tag); // Should already be set by now
p->to = getFrom(&to); // Make sure that if we are sending to the local node, we use our local node addr, not 0
p->channel = to.channel; // Use the same channel that the request came in on
p->hop_limit = routingModule->getHopLimitForResponse(to.hop_start, to.hop_limit);
// No need for an ack if we are just delivering locally (it just generates an ignored ack)
p->want_ack = (to.from != 0) ? to.want_ack : false;
@@ -276,4 +279,4 @@ AdminMessageHandleResult MeshModule::handleAdminMessageForAllPlugins(const mesht
}
}
return handled;
}
}
+2 -1
View File
@@ -153,7 +153,8 @@ class MeshModule
virtual bool wantUIFrame() { return false; }
virtual Observable<const UIFrameEvent *> *getUIFrameObservable() { return NULL; }
meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
meshtastic_MeshPacket *allocAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex,
uint8_t hopStart = 0, uint8_t hopLimit = 0);
/// Send an error response for the specified packet.
meshtastic_MeshPacket *allocErrorResponse(meshtastic_Routing_Error err, const meshtastic_MeshPacket *p);
+25 -18
View File
@@ -1,10 +1,11 @@
#include "configuration.h"
#include <assert.h>
#include <string>
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h"
#endif
#include "../concurrency/Periodic.h"
#include "BluetoothCommon.h" // needed for updateBatteryLevel, FIXME, eventually when we pull mesh out into a lib we shouldn't be whacking bluetooth from here
#include "GPS.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
@@ -15,8 +16,10 @@
#include "modules/NodeInfoModule.h"
#include "modules/PositionModule.h"
#include "power.h"
#include <assert.h>
#include <string>
#ifdef ARCH_ESP32
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
#include "nimble/NimbleBluetooth.h"
#endif
@@ -72,22 +75,23 @@ void MeshService::init()
{
// moved much earlier in boot (called from setup())
// nodeDB.init();
#if HAS_GPS
if (gps)
gpsObserver.observe(&gps->newStatus);
#endif
}
int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp)
{
powerFSM.trigger(EVENT_PACKET_FOR_PHONE); // Possibly keep the node from sleeping
nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
nodeDB->updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
mp->decoded.portnum == meshtastic_PortNum_TELEMETRY_APP && mp->decoded.request_id > 0) {
LOG_DEBUG(
"Received telemetry response. Skip sending our NodeInfo because this potentially a Repeater which will ignore our "
"request for its NodeInfo.\n");
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB.getMeshNode(mp->from)->has_user &&
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user &&
nodeInfoModule) {
LOG_INFO("Heard a node on channel %d we don't know, sending NodeInfo and asking for a response.\n", mp->channel);
nodeInfoModule->sendOurNodeInfo(mp->from, true, mp->channel);
@@ -120,10 +124,10 @@ bool MeshService::reloadConfig(int saveWhat)
// If we can successfully set this radio to these settings, save them to disk
// This will also update the region as needed
bool didReset = nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
bool didReset = nodeDB->resetRadioConfig(); // Don't let the phone send us fatally bad settings
configChanged.notifyObservers(NULL); // This will cause radio hardware to change freqs etc
nodeDB.saveToDisk(saveWhat);
nodeDB->saveToDisk(saveWhat);
return didReset;
}
@@ -133,7 +137,7 @@ void MeshService::reloadOwner(bool shouldSave)
{
// LOG_DEBUG("reloadOwner()\n");
// update our local data directly
nodeDB.updateUser(nodeDB.getNodeNum(), owner);
nodeDB->updateUser(nodeDB->getNodeNum(), owner);
assert(nodeInfoModule);
// update everyone else and save to disk
if (nodeInfoModule && shouldSave) {
@@ -192,7 +196,7 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p)
LOG_WARN("phone tried to pick a nodenum, we don't allow that.\n");
p.from = 0;
} else {
// p.from = nodeDB.getNodeNum();
// p.from = nodeDB->getNodeNum();
}
if (p.id == 0)
@@ -217,7 +221,7 @@ void MeshService::handleToRadio(meshtastic_MeshPacket &p)
/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
bool MeshService::cancelSending(PacketId id)
{
return router->cancelSending(nodeDB.getNodeNum(), id);
return router->cancelSending(nodeDB->getNodeNum(), id);
}
ErrorCode MeshService::sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id)
@@ -245,7 +249,7 @@ ErrorCode MeshService::sendQueueStatusToPhone(const meshtastic_QueueStatus &qs,
void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPhone)
{
uint32_t mesh_packet_id = p->id;
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
nodeDB->updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
ErrorCode res = router->sendLocal(p, src);
@@ -265,16 +269,18 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh
void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
{
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
assert(node);
if (hasValidPosition(node)) {
#if HAS_GPS
if (positionModule) {
LOG_INFO("Sending position ping to 0x%x, wantReplies=%d, channel=%d\n", dest, wantReplies, node->channel);
positionModule->sendOurPosition(dest, wantReplies, node->channel);
}
} else {
#endif
if (nodeInfoModule) {
LOG_INFO("Sending nodeinfo ping to 0x%x, wantReplies=%d, channel=%d\n", dest, wantReplies, node->channel);
nodeInfoModule->sendOurNodeInfo(dest, wantReplies, node->channel);
@@ -320,7 +326,7 @@ void MeshService::sendMqttMessageToClientProxy(meshtastic_MqttClientProxyMessage
meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode()
{
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
assert(node);
// We might not have a position yet for our local node, in that case, at least try to send the time
@@ -344,6 +350,7 @@ meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode()
return node;
}
#if HAS_GPS
int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
{
// Update our local node info with our position (even if we don't decide to update anyone else)
@@ -359,7 +366,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
LOG_DEBUG("onGPSchanged() - lost validLocation\n");
#endif
}
// Used fixed position if configured regalrdless of GPS lock
// Used fixed position if configured regardless of GPS lock
if (config.position.fixed_position) {
LOG_WARN("Using fixed position\n");
pos = TypeConversions::ConvertToPosition(node->position);
@@ -373,11 +380,11 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
pos.longitude_i, pos.altitude);
// Update our current position in the local DB
nodeDB.updatePosition(nodeDB.getNodeNum(), pos, RX_SRC_LOCAL);
nodeDB->updatePosition(nodeDB->getNodeNum(), pos, RX_SRC_LOCAL);
return 0;
}
#endif
bool MeshService::isToPhoneQueueEmpty()
{
return toPhoneQueue.isEmpty();
+4 -2
View File
@@ -23,9 +23,10 @@ extern Allocator<meshtastic_MqttClientProxyMessage> &mqttClientProxyMessagePool;
*/
class MeshService
{
#if HAS_GPS
CallbackObserver<MeshService, const meshtastic::GPSStatus *> gpsObserver =
CallbackObserver<MeshService, const meshtastic::GPSStatus *>(this, &MeshService::onGPSChanged);
#endif
/// received packets waiting for the phone to process them
/// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure
/// we never hang because android hasn't been there in a while
@@ -132,10 +133,11 @@ class MeshService
ErrorCode sendQueueStatusToPhone(const meshtastic_QueueStatus &qs, ErrorCode res, uint32_t mesh_packet_id);
private:
#if HAS_GPS
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh
/// returns 0 to allow further processing
int onGPSChanged(const meshtastic::GPSStatus *arg);
#endif
/// Handle a packet that just arrived from the radio. This method does _ReliableRouternot_ free the provided packet. If it
/// needs to keep the packet around it makes a copy
int handleFromRadio(const meshtastic_MeshPacket *p);
+188 -155
View File
@@ -1,10 +1,12 @@
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h"
#endif
#include "../detect/ScanI2C.h"
#include "Channels.h"
#include "CryptoEngine.h"
#include "Default.h"
#include "FSCommon.h"
#include "GPS.h"
#include "MeshRadio.h"
#include "NodeDB.h"
#include "PacketHistory.h"
@@ -17,11 +19,16 @@
#include "mesh-pb-constants.h"
#include "modules/NeighborInfoModule.h"
#include <ErriezCRC32.h>
#include <algorithm>
#include <iostream>
#include <pb_decode.h>
#include <pb_encode.h>
#include <vector>
#ifdef ARCH_ESP32
#if !MESHTASTIC_EXCLUDE_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
#include "modules/esp32/StoreForwardModule.h"
#include <Preferences.h>
#include <nvs_flash.h>
@@ -36,7 +43,7 @@
#include <utility/bonding.h>
#endif
NodeDB nodeDB;
NodeDB *nodeDB = nullptr;
// we have plenty of ram so statically alloc this tempbuf (for now)
EXT_RAM_ATTR meshtastic_DeviceState devicestate;
@@ -46,6 +53,26 @@ meshtastic_LocalModuleConfig moduleConfig;
meshtastic_ChannelFile channelFile;
meshtastic_OEMStore oemStore;
bool meshtastic_DeviceState_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field)
{
if (ostream) {
std::vector<meshtastic_NodeInfoLite> *vec = (std::vector<meshtastic_NodeInfoLite> *)field->pData;
for (auto item : *vec) {
if (!pb_encode_tag_for_field(ostream, field))
return false;
pb_encode_submessage(ostream, meshtastic_NodeInfoLite_fields, &item);
}
}
if (istream) {
meshtastic_NodeInfoLite node; // this gets good data
std::vector<meshtastic_NodeInfoLite> *vec = (std::vector<meshtastic_NodeInfoLite> *)field->pData;
if (istream->bytes_left && pb_decode(istream, meshtastic_NodeInfoLite_fields, &node))
vec->push_back(node);
}
return true;
}
/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings
* might have changed is incremented. Allows others to detect they might now be on a new channel.
*/
@@ -68,7 +95,63 @@ uint32_t error_address = 0;
static uint8_t ourMacAddr[6];
NodeDB::NodeDB() : meshNodes(devicestate.node_db_lite), numMeshNodes(&devicestate.node_db_lite_count) {}
NodeDB::NodeDB()
{
LOG_INFO("Initializing NodeDB\n");
loadFromDisk();
cleanupMeshDB();
uint32_t devicestateCRC = crc32Buffer(&devicestate, sizeof(devicestate));
uint32_t configCRC = crc32Buffer(&config, sizeof(config));
uint32_t channelFileCRC = crc32Buffer(&channelFile, sizeof(channelFile));
int saveWhat = 0;
// likewise - we always want the app requirements to come from the running appload
myNodeInfo.min_app_version = 30200; // format is Mmmss (where M is 1+the numeric major number. i.e. 30200 means 2.2.00
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
pickNewNodeNum();
// Set our board type so we can share it with others
owner.hw_model = HW_VENDOR;
// Ensure user (nodeinfo) role is set to whatever we're configured to
owner.role = config.device.role;
// Include our owner in the node db under our nodenum
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
info->user = owner;
info->has_user = true;
#ifdef ARCH_ESP32
Preferences preferences;
preferences.begin("meshtastic", false);
myNodeInfo.reboot_count = preferences.getUInt("rebootCounter", 0);
preferences.end();
LOG_DEBUG("Number of Device Reboots: %d\n", myNodeInfo.reboot_count);
#endif
resetRadioConfig(); // If bogus settings got saved, then fix them
// nodeDB->LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d\n", config.lora.region, myNodeInfo.my_node_num, numMeshNodes);
if (devicestateCRC != crc32Buffer(&devicestate, sizeof(devicestate)))
saveWhat |= SEGMENT_DEVICESTATE;
if (configCRC != crc32Buffer(&config, sizeof(config)))
saveWhat |= SEGMENT_CONFIG;
if (channelFileCRC != crc32Buffer(&channelFile, sizeof(channelFile)))
saveWhat |= SEGMENT_CHANNELS;
if (!devicestate.node_remote_hardware_pins) {
meshtastic_NodeRemoteHardwarePin empty[12] = {meshtastic_RemoteHardwarePin_init_default};
memcpy(devicestate.node_remote_hardware_pins, empty, sizeof(empty));
}
if (config.position.gps_enabled) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
config.position.gps_enabled = 0;
}
saveToDisk(saveWhat);
}
/**
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on
@@ -76,7 +159,7 @@ NodeDB::NodeDB() : meshNodes(devicestate.node_db_lite), numMeshNodes(&devicestat
*/
NodeNum getFrom(const meshtastic_MeshPacket *p)
{
return (p->from == 0) ? nodeDB.getNodeNum() : p->from;
return (p->from == 0) ? nodeDB->getNodeNum() : p->from;
}
bool NodeDB::resetRadioConfig(bool factory_reset)
@@ -97,22 +180,6 @@ bool NodeDB::resetRadioConfig(bool factory_reset)
channels.onConfigChanged();
// temp hack for quicker testing
// devicestate.no_save = true;
if (devicestate.no_save) {
LOG_DEBUG("***** DEVELOPMENT MODE - DO NOT RELEASE *****\n");
// Sleep quite frequently to stress test the BLE comms, broadcast position every 6 mins
config.display.screen_on_secs = 10;
config.power.wait_bluetooth_secs = 10;
config.position.position_broadcast_secs = 6 * 60;
config.power.ls_secs = 60;
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_TW;
// Enter super deep sleep soon and stay there not very long
// radioConfig.preferences.sds_secs = 60;
}
// Update the global myRegion
initRegion();
@@ -130,6 +197,9 @@ bool NodeDB::factoryReset()
LOG_INFO("Performing factory reset!\n");
// first, remove the "/prefs" (this removes most prefs)
rmDir("/prefs");
if (FSCom.exists("/static/rangetest.csv") && !FSCom.remove("/static/rangetest.csv")) {
LOG_ERROR("Could not remove rangetest.csv file\n");
}
// second, install default state (this will deal with the duplicate mac address issue)
installDefaultDeviceState();
installDefaultConfig();
@@ -163,7 +233,7 @@ void NodeDB::installDefaultConfig()
config.has_position = true;
config.has_power = true;
config.has_network = true;
config.has_bluetooth = true;
config.has_bluetooth = (HAS_BLUETOOTH ? true : false);
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_ALL;
config.lora.sx126x_rx_boosted_gain = true;
@@ -196,7 +266,7 @@ void NodeDB::installDefaultConfig()
config.position.broadcast_smart_minimum_distance = 100;
config.position.broadcast_smart_minimum_interval_secs = 30;
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER)
config.device.node_info_broadcast_secs = 3 * 60 * 60;
config.device.node_info_broadcast_secs = default_node_info_broadcast_secs;
config.device.serial_enabled = true;
resetRadioConfig();
strncpy(config.network.ntp_server, "0.pool.ntp.org", 32);
@@ -365,8 +435,9 @@ void NodeDB::installDefaultChannels()
void NodeDB::resetNodes()
{
devicestate.node_db_lite_count = 1;
std::fill(&devicestate.node_db_lite[1], &devicestate.node_db_lite[MAX_NUM_NODES - 1], meshtastic_NodeInfoLite());
numMeshNodes = 1;
std::fill(devicestate.node_db_lite.begin() + 1, devicestate.node_db_lite.end(), meshtastic_NodeInfoLite());
clearLocalPosition();
saveDeviceStateToDisk();
if (neighborInfoModule && moduleConfig.neighbor_info.enabled)
neighborInfoModule->resetNeighbors();
@@ -375,41 +446,56 @@ void NodeDB::resetNodes()
void NodeDB::removeNodeByNum(uint nodeNum)
{
int newPos = 0, removed = 0;
for (int i = 0; i < *numMeshNodes; i++) {
if (meshNodes[i].num != nodeNum)
meshNodes[newPos++] = meshNodes[i];
for (int i = 0; i < numMeshNodes; i++) {
if (meshNodes->at(i).num != nodeNum)
meshNodes->at(newPos++) = meshNodes->at(i);
else
removed++;
}
*numMeshNodes -= removed;
numMeshNodes -= removed;
std::fill(devicestate.node_db_lite.begin() + numMeshNodes, devicestate.node_db_lite.begin() + numMeshNodes + 1,
meshtastic_NodeInfoLite());
LOG_DEBUG("NodeDB::removeNodeByNum purged %d entries. Saving changes...\n", removed);
saveDeviceStateToDisk();
}
void NodeDB::clearLocalPosition()
{
meshtastic_NodeInfoLite *node = getMeshNode(nodeDB->getNodeNum());
node->position.latitude_i = 0;
node->position.longitude_i = 0;
node->position.altitude = 0;
node->position.time = 0;
setLocalPosition(meshtastic_Position_init_default);
}
void NodeDB::cleanupMeshDB()
{
int newPos = 0, removed = 0;
for (int i = 0; i < *numMeshNodes; i++) {
if (meshNodes[i].has_user)
meshNodes[newPos++] = meshNodes[i];
for (int i = 0; i < numMeshNodes; i++) {
if (meshNodes->at(i).has_user)
meshNodes->at(newPos++) = meshNodes->at(i);
else
removed++;
}
*numMeshNodes -= removed;
numMeshNodes -= removed;
std::fill(devicestate.node_db_lite.begin() + numMeshNodes, devicestate.node_db_lite.begin() + numMeshNodes + removed,
meshtastic_NodeInfoLite());
LOG_DEBUG("cleanupMeshDB purged %d entries\n", removed);
}
void NodeDB::installDefaultDeviceState()
{
LOG_INFO("Installing default DeviceState\n");
memset(&devicestate, 0, sizeof(meshtastic_DeviceState));
// memset(&devicestate, 0, sizeof(meshtastic_DeviceState));
*numMeshNodes = 0;
numMeshNodes = 0;
meshNodes = &devicestate.node_db_lite;
// init our devicestate with valid flags so protobuf writing/reading will work
devicestate.has_my_node = true;
devicestate.has_owner = true;
devicestate.node_db_lite_count = 0;
// devicestate.node_db_lite_count = 0;
devicestate.version = DEVICESTATE_CUR_VER;
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
@@ -423,65 +509,6 @@ void NodeDB::installDefaultDeviceState()
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
}
void NodeDB::init()
{
LOG_INFO("Initializing NodeDB\n");
loadFromDisk();
cleanupMeshDB();
uint32_t devicestateCRC = crc32Buffer(&devicestate, sizeof(devicestate));
uint32_t configCRC = crc32Buffer(&config, sizeof(config));
uint32_t channelFileCRC = crc32Buffer(&channelFile, sizeof(channelFile));
int saveWhat = 0;
// likewise - we always want the app requirements to come from the running appload
myNodeInfo.min_app_version = 30200; // format is Mmmss (where M is 1+the numeric major number. i.e. 30200 means 2.2.00
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
pickNewNodeNum();
// Set our board type so we can share it with others
owner.hw_model = HW_VENDOR;
// Ensure user (nodeinfo) role is set to whatever we're configured to
owner.role = config.device.role;
// Include our owner in the node db under our nodenum
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
info->user = owner;
info->has_user = true;
#ifdef ARCH_ESP32
Preferences preferences;
preferences.begin("meshtastic", false);
myNodeInfo.reboot_count = preferences.getUInt("rebootCounter", 0);
preferences.end();
LOG_DEBUG("Number of Device Reboots: %d\n", myNodeInfo.reboot_count);
#endif
resetRadioConfig(); // If bogus settings got saved, then fix them
LOG_DEBUG("region=%d, NODENUM=0x%x, dbsize=%d\n", config.lora.region, myNodeInfo.my_node_num, *numMeshNodes);
if (devicestateCRC != crc32Buffer(&devicestate, sizeof(devicestate)))
saveWhat |= SEGMENT_DEVICESTATE;
if (configCRC != crc32Buffer(&config, sizeof(config)))
saveWhat |= SEGMENT_CONFIG;
if (channelFileCRC != crc32Buffer(&channelFile, sizeof(channelFile)))
saveWhat |= SEGMENT_CHANNELS;
if (!devicestate.node_remote_hardware_pins) {
meshtastic_NodeRemoteHardwarePin empty[12] = {meshtastic_RemoteHardwarePin_init_default};
memcpy(devicestate.node_remote_hardware_pins, empty, sizeof(empty));
}
if (config.position.gps_enabled) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
config.position.gps_enabled = 0;
}
saveToDisk(saveWhat);
}
// We reserve a few nodenums for future use
#define NUM_RESERVED 4
@@ -503,7 +530,7 @@ void NodeDB::pickNewNodeNum()
LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, so trying for 0x%x\n", nodeNum, candidate);
nodeNum = candidate;
}
LOG_WARN("Using nodenum 0x%x \n", nodeNum);
LOG_DEBUG("Using nodenum 0x%x \n", nodeNum);
myNodeInfo.my_node_num = nodeNum;
}
@@ -549,17 +576,21 @@ bool NodeDB::loadProto(const char *filename, size_t protoSize, size_t objSize, c
void NodeDB::loadFromDisk()
{
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
if (!loadProto(prefFileName, meshtastic_DeviceState_size, sizeof(meshtastic_DeviceState), &meshtastic_DeviceState_msg,
&devicestate)) {
if (!loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES * sizeof(meshtastic_NodeInfo),
sizeof(meshtastic_DeviceState), &meshtastic_DeviceState_msg, &devicestate)) {
installDefaultDeviceState(); // Our in RAM copy might now be corrupt
} else {
if (devicestate.version < DEVICESTATE_MIN_VER) {
LOG_WARN("Devicestate %d is old, discarding\n", devicestate.version);
factoryReset();
} else {
LOG_INFO("Loaded saved devicestate version %d\n", devicestate.version);
LOG_INFO("Loaded saved devicestate version %d, with nodecount: %d\n", devicestate.version,
devicestate.node_db_lite.size());
meshNodes = &devicestate.node_db_lite;
numMeshNodes = devicestate.node_db_lite.size();
}
}
meshNodes->resize(MAX_NUM_NODES);
if (!loadProto(configFileName, meshtastic_LocalConfig_size, sizeof(meshtastic_LocalConfig), &meshtastic_LocalConfig_msg,
&config)) {
@@ -638,7 +669,7 @@ bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_
if (failedCounter >= 2) {
FSCom.format();
// After formatting, the device needs to be restarted
nodeDB.resetRadioConfig(true);
nodeDB->resetRadioConfig(true);
}
#endif
}
@@ -650,68 +681,61 @@ bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_
void NodeDB::saveChannelsToDisk()
{
if (!devicestate.no_save) {
#ifdef FSCom
FSCom.mkdir("/prefs");
FSCom.mkdir("/prefs");
#endif
saveProto(channelFileName, meshtastic_ChannelFile_size, &meshtastic_ChannelFile_msg, &channelFile);
}
saveProto(channelFileName, meshtastic_ChannelFile_size, &meshtastic_ChannelFile_msg, &channelFile);
}
void NodeDB::saveDeviceStateToDisk()
{
if (!devicestate.no_save) {
#ifdef FSCom
FSCom.mkdir("/prefs");
FSCom.mkdir("/prefs");
#endif
saveProto(prefFileName, meshtastic_DeviceState_size, &meshtastic_DeviceState_msg, &devicestate);
}
saveProto(prefFileName, sizeof(devicestate) + numMeshNodes * meshtastic_NodeInfoLite_size, &meshtastic_DeviceState_msg,
&devicestate);
}
void NodeDB::saveToDisk(int saveWhat)
{
if (!devicestate.no_save) {
#ifdef FSCom
FSCom.mkdir("/prefs");
FSCom.mkdir("/prefs");
#endif
if (saveWhat & SEGMENT_DEVICESTATE) {
saveDeviceStateToDisk();
}
if (saveWhat & SEGMENT_DEVICESTATE) {
saveDeviceStateToDisk();
}
if (saveWhat & SEGMENT_CONFIG) {
config.has_device = true;
config.has_display = true;
config.has_lora = true;
config.has_position = true;
config.has_power = true;
config.has_network = true;
config.has_bluetooth = true;
saveProto(configFileName, meshtastic_LocalConfig_size, &meshtastic_LocalConfig_msg, &config);
}
if (saveWhat & SEGMENT_CONFIG) {
config.has_device = true;
config.has_display = true;
config.has_lora = true;
config.has_position = true;
config.has_power = true;
config.has_network = true;
config.has_bluetooth = true;
saveProto(configFileName, meshtastic_LocalConfig_size, &meshtastic_LocalConfig_msg, &config);
}
if (saveWhat & SEGMENT_MODULECONFIG) {
moduleConfig.has_canned_message = true;
moduleConfig.has_external_notification = true;
moduleConfig.has_mqtt = true;
moduleConfig.has_range_test = true;
moduleConfig.has_serial = true;
moduleConfig.has_store_forward = true;
moduleConfig.has_telemetry = true;
saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig);
}
if (saveWhat & SEGMENT_MODULECONFIG) {
moduleConfig.has_canned_message = true;
moduleConfig.has_external_notification = true;
moduleConfig.has_mqtt = true;
moduleConfig.has_range_test = true;
moduleConfig.has_serial = true;
moduleConfig.has_store_forward = true;
moduleConfig.has_telemetry = true;
saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig);
}
if (saveWhat & SEGMENT_CHANNELS) {
saveChannelsToDisk();
}
} else {
LOG_DEBUG("***** DEVELOPMENT MODE - DO NOT RELEASE - not saving to flash *****\n");
if (saveWhat & SEGMENT_CHANNELS) {
saveChannelsToDisk();
}
}
const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex)
{
if (readIndex < *numMeshNodes)
return &meshNodes[readIndex++];
if (readIndex < numMeshNodes)
return &meshNodes->at(readIndex++);
else
return NULL;
}
@@ -741,14 +765,17 @@ uint32_t sinceReceived(const meshtastic_MeshPacket *p)
#define NUM_ONLINE_SECS (60 * 60 * 2) // 2 hrs to consider someone offline
size_t NodeDB::getNumOnlineMeshNodes()
size_t NodeDB::getNumOnlineMeshNodes(bool localOnly)
{
size_t numseen = 0;
// FIXME this implementation is kinda expensive
for (int i = 0; i < *numMeshNodes; i++)
if (sinceLastSeen(&meshNodes[i]) < NUM_ONLINE_SECS)
for (int i = 0; i < numMeshNodes; i++) {
if (localOnly && meshNodes->at(i).via_mqtt)
continue;
if (sinceLastSeen(&meshNodes->at(i)) < NUM_ONLINE_SECS)
numseen++;
}
return numseen;
}
@@ -872,6 +899,12 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
if (mp.rx_snr)
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT
// If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway
if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start)
info->hops_away = mp.hop_start - mp.hop_limit;
}
}
@@ -888,9 +921,9 @@ uint8_t NodeDB::getMeshNodeChannel(NodeNum n)
/// NOTE: This function might be called from an ISR
meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n)
{
for (int i = 0; i < *numMeshNodes; i++)
if (meshNodes[i].num == n)
return &meshNodes[i];
for (int i = 0; i < numMeshNodes; i++)
if (meshNodes->at(i).num == n)
return &meshNodes->at(i);
return NULL;
}
@@ -901,27 +934,27 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
meshtastic_NodeInfoLite *lite = getMeshNode(n);
if (!lite) {
if ((*numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
if (screen)
screen->print("Warn: node database full!\nErasing oldest entry\n");
LOG_INFO("Warn: node database full!\nErasing oldest entry\n");
LOG_WARN("Node database full! Erasing oldest entry\n");
// look for oldest node and erase it
uint32_t oldest = UINT32_MAX;
int oldestIndex = -1;
for (int i = 1; i < *numMeshNodes; i++) {
if (meshNodes[i].last_heard < oldest) {
oldest = meshNodes[i].last_heard;
for (int i = 1; i < numMeshNodes; i++) {
if (!meshNodes->at(i).is_favorite && meshNodes->at(i).last_heard < oldest) {
oldest = meshNodes->at(i).last_heard;
oldestIndex = i;
}
}
// Shove the remaining nodes down the chain
for (int i = oldestIndex; i < *numMeshNodes - 1; i++) {
meshNodes[i] = meshNodes[i + 1];
for (int i = oldestIndex; i < numMeshNodes - 1; i++) {
meshNodes->at(i) = meshNodes->at(i + 1);
}
(*numMeshNodes)--;
(numMeshNodes)--;
}
// add the node at the end
lite = &meshNodes[(*numMeshNodes)++];
lite = &meshNodes->at((numMeshNodes)++);
// everything is missing except the nodenum
memset(lite, 0, sizeof(*lite));
+21 -52
View File
@@ -3,6 +3,7 @@
#include "Observer.h"
#include <Arduino.h>
#include <assert.h>
#include <vector>
#include "MeshTypes.h"
#include "NodeStatus.h"
@@ -45,21 +46,18 @@ class NodeDB
// Eventually use a smarter datastructure
// HashMap<NodeNum, NodeInfo> nodes;
// Note: these two references just point into our static array we serialize to/from disk
meshtastic_NodeInfoLite *meshNodes;
pb_size_t *numMeshNodes;
public:
std::vector<meshtastic_NodeInfoLite> *meshNodes;
bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled
meshtastic_NodeInfoLite *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI
Observable<const meshtastic::NodeStatus *> newStatus;
pb_size_t numMeshNodes;
/// don't do mesh based algorithm for node id assignment (initially)
/// instead just store in flash - possibly even in the initial alpha release do this hack
NodeDB();
/// Called from service after app start, to do init which can only be done after OS load
void init();
/// write to flash
void saveToDisk(int saveWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS),
saveChannelsToDisk(), saveDeviceStateToDisk();
@@ -108,8 +106,10 @@ class NodeDB
// get channel channel index we heard a nodeNum on, defaults to 0 if not found
uint8_t getMeshNodeChannel(NodeNum n);
/// Return the number of nodes we've heard from recently (within the last 2 hrs?)
size_t getNumOnlineMeshNodes();
/* Return the number of nodes we've heard from recently (within the last 2 hrs?)
* @param localOnly if true, ignore nodes heard via MQTT
*/
size_t getNumOnlineMeshNodes(bool localOnly = false);
void initConfigIntervals(), initModuleConfigIntervals(), resetNodes(), removeNodeByNum(uint nodeNum);
@@ -124,15 +124,22 @@ class NodeDB
meshtastic_NodeInfoLite *getMeshNodeByIndex(size_t x)
{
assert(x < *numMeshNodes);
return &meshNodes[x];
assert(x < numMeshNodes);
return &meshNodes->at(x);
}
meshtastic_NodeInfoLite *getMeshNode(NodeNum n);
size_t getNumMeshNodes() { return *numMeshNodes; }
size_t getNumMeshNodes() { return numMeshNodes; }
void setLocalPosition(meshtastic_Position position)
void clearLocalPosition();
void setLocalPosition(meshtastic_Position position, bool timeOnly = false)
{
if (timeOnly) {
LOG_DEBUG("Setting local position time only: time=%i\n", position.time);
localPosition.time = position.time;
return;
}
LOG_DEBUG("Setting local position: latitude=%i, longitude=%i, time=%i\n", position.latitude_i, position.longitude_i,
position.time);
localPosition = position;
@@ -160,7 +167,7 @@ class NodeDB
void installDefaultDeviceState(), installDefaultChannels(), installDefaultConfig(), installDefaultModuleConfig();
};
extern NodeDB nodeDB;
extern NodeDB *nodeDB;
/*
If is_router is set, we use a number of different default values
@@ -184,46 +191,6 @@ extern NodeDB nodeDB;
// Our delay functions check for this for times that should never expire
#define NODE_DELAY_FOREVER 0xffffffff
#define IF_ROUTER(routerVal, normalVal) \
((config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER) ? (routerVal) : (normalVal))
#define ONE_DAY 24 * 60 * 60
#define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60)
#define default_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 15 * 60)
#define default_wait_bluetooth_secs IF_ROUTER(1, 60)
#define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep
#define default_ls_secs IF_ROUTER(ONE_DAY, 5 * 60)
#define default_min_wake_secs 10
#define default_screen_on_secs IF_ROUTER(1, 60 * 10)
#define default_mqtt_address "mqtt.meshtastic.org"
#define default_mqtt_username "meshdev"
#define default_mqtt_password "large4cats"
#define default_mqtt_root "msh"
inline uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval)
{
if (configuredInterval > 0)
return configuredInterval * 1000;
return default_broadcast_interval_secs * 1000;
}
inline uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval)
{
if (configuredInterval > 0)
return configuredInterval * 1000;
return defaultInterval * 1000;
}
inline uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue)
{
if (configured > 0)
return configured;
return defaultValue;
}
/// Sometimes we will have Position objects that only have a time, so check for
/// valid lat/lon
static inline bool hasValidPosition(const meshtastic_NodeInfoLite *n)
@@ -247,3 +214,5 @@ extern uint32_t error_address;
(ModuleConfig_CannedMessageConfig_size + ModuleConfig_ExternalNotificationConfig_size + ModuleConfig_MQTTConfig_size + \
ModuleConfig_RangeTestConfig_size + ModuleConfig_SerialConfig_size + ModuleConfig_StoreForwardConfig_size + \
ModuleConfig_TelemetryConfig_size + ModuleConfig_size)
// Please do not remove this comment, it makes trunk and compiler happy at the same time.
+4
View File
@@ -2,6 +2,10 @@
#include "configuration.h"
#include "mesh-pb-constants.h"
#ifdef ARCH_PORTDUINO
#include "platform/portduino/PortduinoGlue.h"
#endif
PacketHistory::PacketHistory()
{
recentPackets.reserve(MAX_NUM_NODES); // Prealloc the worst case # of records - to prevent heap fragmentation
+20 -6
View File
@@ -1,12 +1,16 @@
#include "PhoneAPI.h"
#include "Channels.h"
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h"
#endif
#include "Channels.h"
#include "Default.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PhoneAPI.h"
#include "PowerFSM.h"
#include "RadioInterface.h"
#include "TypeConversions.h"
#include "configuration.h"
#include "main.h"
#include "xmodem.h"
@@ -17,8 +21,9 @@
#if ToRadio_size > MAX_TO_FROM_RADIO_SIZE
#error ToRadio is too big
#endif
#if !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h"
#endif
PhoneAPI::PhoneAPI()
{
@@ -103,12 +108,21 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
LOG_INFO("Got xmodem packet\n");
xModem.handlePacket(toRadioScratch.xmodemPacket);
break;
#if !MESHTASTIC_EXCLUDE_MQTT
case meshtastic_ToRadio_mqttClientProxyMessage_tag:
LOG_INFO("Got MqttClientProxy message\n");
if (mqtt && moduleConfig.mqtt.proxy_to_client_enabled) {
if (mqtt && moduleConfig.mqtt.proxy_to_client_enabled && moduleConfig.mqtt.enabled &&
(channels.anyMqttEnabled() || moduleConfig.mqtt.map_reporting_enabled)) {
mqtt->onClientProxyReceive(toRadioScratch.mqttClientProxyMessage);
} else {
LOG_WARN("MqttClientProxy received but proxy is not enabled, no channels have up/downlink, or map reporting "
"not enabled\n");
}
break;
#endif
case meshtastic_ToRadio_heartbeat_tag:
LOG_DEBUG("Got client heartbeat\n");
break;
default:
// Ignore nop messages
// LOG_DEBUG("Error: unexpected ToRadio variant\n");
@@ -413,7 +427,7 @@ bool PhoneAPI::available()
case STATE_SEND_NODEINFO:
if (nodeInfoForPhone.num == 0) {
auto nextNode = nodeDB.readNextMeshNode(readIndex);
auto nextNode = nodeDB->readNextMeshNode(readIndex);
if (nextNode) {
nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(nextNode);
}
+3 -3
View File
@@ -56,7 +56,7 @@ template <class T> class ProtobufModule : protected SinglePortModule
*/
const char *getSenderShortName(const meshtastic_MeshPacket &mp)
{
auto node = nodeDB.getMeshNode(getFrom(&mp));
auto node = nodeDB->getMeshNode(getFrom(&mp));
const char *sender = (node) ? node->user.short_name : "???";
return sender;
}
@@ -108,8 +108,8 @@ template <class T> class ProtobufModule : protected SinglePortModule
// if we can't decode it, nobody can process it!
return;
}
}
return alterReceivedProtobuf(mp, decoded);
return alterReceivedProtobuf(mp, decoded);
}
}
};
+1 -1
View File
@@ -19,7 +19,7 @@ RF95Interface::RF95Interface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs, RADIO
RADIOLIB_PIN_TYPE busy)
: RadioLibInterface(hal, cs, irq, rst, busy)
{
LOG_WARN("RF95Interface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy);
LOG_DEBUG("RF95Interface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy);
}
/** Some boards require GPIO control of tx vs rx paths */
+14 -3
View File
@@ -1,5 +1,6 @@
#include "RadioInterface.h"
#include "Channels.h"
#include "DisplayFormatters.h"
#include "MeshRadio.h"
#include "MeshService.h"
#include "NodeDB.h"
@@ -143,6 +144,7 @@ const RegionInfo regions[] = {
};
const RegionInfo *myRegion;
bool RadioInterface::uses_default_frequency_slot = true;
static uint8_t bytes[MAX_RHPACKETLEN];
@@ -302,6 +304,8 @@ void printPacket(const char *prefix, const meshtastic_MeshPacket *p)
out += DEBUG_PORT.mt_sprintf(" rxRSSI=%i", p->rx_rssi);
if (p->via_mqtt != 0)
out += DEBUG_PORT.mt_sprintf(" via MQTT");
if (p->hop_start != 0)
out += DEBUG_PORT.mt_sprintf(" hopStart=%d", p->hop_start);
if (p->priority != 0)
out += DEBUG_PORT.mt_sprintf(" priority=%d", p->priority);
@@ -330,8 +334,8 @@ bool RadioInterface::init()
notifyDeepSleepObserver.observe(&notifyDeepSleep);
// we now expect interfaces to operate in promiscuous mode
// radioIf.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor
// time.
// radioIf.setThisAddress(nodeDB->getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at
// constructor time.
applyModemConfig();
@@ -484,6 +488,10 @@ void RadioInterface::applyModemConfig()
// channel_num is actually (channel_num - 1), since modulus (%) returns values from 0 to (numChannels - 1)
int channel_num = (loraConfig.channel_num ? loraConfig.channel_num - 1 : hash(channelName)) % numChannels;
// Check if we use the default frequency slot
RadioInterface::uses_default_frequency_slot =
channel_num == hash(DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false)) % numChannels;
// Old frequency selection formula
// float freq = myRegion->freqStart + ((((myRegion->freqEnd - myRegion->freqStart) / numChannels) / 2) * channel_num);
@@ -556,11 +564,14 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p)
h->to = p->to;
h->id = p->id;
h->channel = p->channel;
h->next_hop = 0; // *** For future use ***
h->relay_node = 0; // *** For future use ***
if (p->hop_limit > HOP_MAX) {
LOG_WARN("hop limit %d is too high, setting to %d\n", p->hop_limit, HOP_RELIABLE);
p->hop_limit = HOP_RELIABLE;
}
h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0) | (p->via_mqtt ? PACKET_FLAGS_VIA_MQTT_MASK : 0);
h->flags |= (p->hop_start << PACKET_FLAGS_HOP_START_SHIFT) & PACKET_FLAGS_HOP_START_MASK;
// if the sender nodenum is zero, that means uninitialized
assert(h->from);
@@ -569,4 +580,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p)
sendingPacket = p;
return p->encrypted.size + sizeof(PacketHeader);
}
}
+13 -2
View File
@@ -10,9 +10,11 @@
#define MAX_RHPACKETLEN 256
#define PACKET_FLAGS_HOP_MASK 0x07
#define PACKET_FLAGS_HOP_LIMIT_MASK 0x07
#define PACKET_FLAGS_WANT_ACK_MASK 0x08
#define PACKET_FLAGS_VIA_MQTT_MASK 0x10
#define PACKET_FLAGS_HOP_START_MASK 0xE0
#define PACKET_FLAGS_HOP_START_SHIFT 5
/**
* This structure has to exactly match the wire layout when sent over the radio link. Used to keep compatibility
@@ -32,6 +34,12 @@ typedef struct {
/** The channel hash - used as a hint for the decoder to limit which channels we consider */
uint8_t channel;
// ***For future use*** Last byte of the NodeNum of the next-hop for this packet
uint8_t next_hop;
// ***For future use*** Last byte of the NodeNum of the node that will relay/relayed this packet
uint8_t relay_node;
} PacketHeader;
/**
@@ -173,6 +181,9 @@ class RadioInterface
/// Some boards (1st gen Pinetab Lora module) have broken IRQ wires, so we need to poll via i2c registers
virtual bool isIRQPending() { return false; }
// Whether we use the default frequency slot given our LoRa config (region and modem preset)
static bool uses_default_frequency_slot;
protected:
int8_t power = 17; // Set by applyModemConfig()
@@ -224,4 +235,4 @@ class RadioInterface
};
/// Debug printing for packets
void printPacket(const char *prefix, const meshtastic_MeshPacket *p);
void printPacket(const char *prefix, const meshtastic_MeshPacket *p);
+4 -3
View File
@@ -359,8 +359,9 @@ void RadioLibInterface::handleReceiveInterrupt()
mp->to = h->to;
mp->id = h->id;
mp->channel = h->channel;
assert(HOP_MAX <= PACKET_FLAGS_HOP_MASK); // If hopmax changes, carefully check this code
mp->hop_limit = h->flags & PACKET_FLAGS_HOP_MASK;
assert(HOP_MAX <= PACKET_FLAGS_HOP_LIMIT_MASK); // If hopmax changes, carefully check this code
mp->hop_limit = h->flags & PACKET_FLAGS_HOP_LIMIT_MASK;
mp->hop_start = (h->flags & PACKET_FLAGS_HOP_START_MASK) >> PACKET_FLAGS_HOP_START_SHIFT;
mp->want_ack = !!(h->flags & PACKET_FLAGS_WANT_ACK_MASK);
mp->via_mqtt = !!(h->flags & PACKET_FLAGS_VIA_MQTT_MASK);
@@ -407,4 +408,4 @@ void RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
// bits
enableInterrupt(isrTxLevel0);
}
}
}
+9 -8
View File
@@ -71,12 +71,12 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
i->second.nextTxMsec += iface->getPacketTime(p);
}
/* Resend implicit ACKs for repeated packets (assuming the original packet was sent with HOP_RELIABLE)
/* Resend implicit ACKs for repeated packets (hopStart equals hopLimit);
* this way if an implicit ACK is dropped and a packet is resent we'll rebroadcast again.
* Resending real ACKs is omitted, as you might receive a packet multiple times due to flooding and
* flooding this ACK back to the original sender already adds redundancy. */
if (wasSeenRecently(p, false) && p->hop_limit == HOP_RELIABLE && !MeshModule::currentReply && p->to != nodeDB.getNodeNum()) {
// retransmission on broadcast has hop_limit still equal to HOP_RELIABLE
bool isRepeated = p->hop_start == 0 ? (p->hop_limit == HOP_RELIABLE) : (p->hop_start == p->hop_limit);
if (wasSeenRecently(p, false) && isRepeated && !MeshModule::currentReply && p->to != nodeDB->getNodeNum()) {
LOG_DEBUG("Resending implicit ack for a repeated floodmsg\n");
meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p);
tosend->hop_limit--; // bump down the hop count
@@ -107,10 +107,11 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
if (MeshModule::currentReply) {
LOG_DEBUG("Some other module has replied to this message, no need for a 2nd ack\n");
} else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel);
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, p->hop_start, p->hop_limit);
} else {
// Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded
sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex());
sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(), p->hop_start,
p->hop_limit);
}
}
@@ -166,8 +167,6 @@ bool ReliableRouter::stopRetransmission(GlobalPacketId key)
auto old = findPendingPacket(key);
if (old) {
auto p = old->packet;
auto numErased = pending.erase(key);
assert(numErased == 1);
/* Only when we already transmitted a packet via LoRa, we will cancel the packet in the Tx queue
to avoid canceling a transmission if it was ACKed super fast via MQTT */
if (old->numRetransmissions < NUM_RETRANSMISSIONS - 1) {
@@ -176,6 +175,8 @@ bool ReliableRouter::stopRetransmission(GlobalPacketId key)
// now free the pooled copy for retransmission too
packetPool.release(p);
}
auto numErased = pending.erase(key);
assert(numErased == 1);
return true;
} else
return false;
@@ -255,4 +256,4 @@ void ReliableRouter::setNextTx(PendingPacket *pending)
LOG_DEBUG("Setting next retransmission in %u msecs: ", d);
printPacket("", pending->packet);
setReceivedMessage(); // Run ASAP, so we can figure out our correct sleep time
}
}
+35 -16
View File
@@ -11,9 +11,9 @@
extern "C" {
#include "mesh/compression/unishox2.h"
}
#if !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h"
#endif
/**
* Router todo
*
@@ -119,7 +119,7 @@ meshtastic_MeshPacket *Router::allocForSending()
meshtastic_MeshPacket *p = packetPool.allocZeroed();
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // Assume payload is decoded at start.
p->from = nodeDB.getNodeNum();
p->from = nodeDB->getNodeNum();
p->to = NODENUM_BROADCAST;
p->hop_limit = (config.lora.hop_limit >= HOP_MAX) ? HOP_MAX : config.lora.hop_limit;
p->id = generatePacketId();
@@ -132,9 +132,10 @@ meshtastic_MeshPacket *Router::allocForSending()
/**
* Send an ack or a nak packet back towards whoever sent idFrom
*/
void Router::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
void Router::sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopStart,
uint8_t hopLimit)
{
routingModule->sendAckNak(err, to, idFrom, chIndex);
routingModule->sendAckNak(err, to, idFrom, chIndex, hopStart, hopLimit);
}
void Router::abortSendAndNak(meshtastic_Routing_Error err, meshtastic_MeshPacket *p)
@@ -164,7 +165,7 @@ meshtastic_QueueStatus Router::getQueueStatus()
ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
{
// No need to deliver externally if the destination is the local node
if (p->to == nodeDB.getNodeNum()) {
if (p->to == nodeDB->getNodeNum()) {
printPacket("Enqueued local", p);
enqueueReceivedMessage(p);
return ERRNO_OK;
@@ -181,7 +182,7 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
}
if (!p->channel) { // don't override if a channel was requested
p->channel = nodeDB.getMeshNodeChannel(p->to);
p->channel = nodeDB->getMeshNodeChannel(p->to);
LOG_DEBUG("localSend to channel %d\n", p->channel);
}
@@ -204,7 +205,7 @@ void printBytes(const char *label, const uint8_t *p, size_t numbytes)
*/
ErrorCode Router::send(meshtastic_MeshPacket *p)
{
if (p->to == nodeDB.getNodeNum()) {
if (p->to == nodeDB->getNodeNum()) {
LOG_ERROR("BUG! send() called with packet destined for local node!\n");
packetPool.release(p);
return meshtastic_Routing_Error_BAD_REQUEST;
@@ -219,7 +220,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes);
#endif
meshtastic_Routing_Error err = meshtastic_Routing_Error_DUTY_CYCLE_LIMIT;
if (getFrom(p) == nodeDB.getNodeNum()) { // only send NAK to API, not to the mesh
if (getFrom(p) == nodeDB->getNodeNum()) { // only send NAK to API, not to the mesh
abortSendAndNak(err, p);
} else {
packetPool.release(p);
@@ -240,6 +241,10 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
// the lora we need to make sure we have replaced it with our local address
p->from = getFrom(p);
// If we are the original transmitter, set the hop limit with which we start
if (p->from == getNodeNum())
p->hop_start = p->hop_limit;
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
assert(p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag ||
@@ -256,11 +261,12 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
abortSendAndNak(encodeResult, p);
return encodeResult; // FIXME - this isn't a valid ErrorCode
}
#if !MESHTASTIC_EXCLUDE_MQTT
// Only publish to MQTT if we're the original transmitter of the packet
if (moduleConfig.mqtt.enabled && p->from == nodeDB.getNodeNum() && mqtt) {
if (moduleConfig.mqtt.enabled && p->from == nodeDB->getNodeNum() && mqtt) {
mqtt->onSend(*p, *p_decoded, chIndex);
}
#endif
packetPool.release(p_decoded);
}
@@ -292,8 +298,8 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
return false;
if (config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY &&
!nodeDB.getMeshNode(p->from)->has_user) {
LOG_DEBUG("Node 0x%x not in NodeDB. Rebroadcast mode KNOWN_ONLY will ignore packet\n", p->from);
(nodeDB->getMeshNode(p->from) == NULL || !nodeDB->getMeshNode(p->from)->has_user)) {
LOG_DEBUG("Node 0x%x not in nodeDB-> Rebroadcast mode KNOWN_ONLY will ignore packet\n", p->from);
return false;
}
@@ -426,7 +432,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
NodeNum Router::getNodeNum()
{
return nodeDB.getNodeNum();
return nodeDB->getNodeNum();
}
/**
@@ -435,6 +441,7 @@ NodeNum Router::getNodeNum()
*/
void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
{
bool skipHandle = false;
// Also, we should set the time from the ISR and it should have msec level resolution
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
// Store a copy of encrypted packet for MQTT
@@ -451,9 +458,20 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
else
printPacket("handleReceived(REMOTE)", p);
// Neighbor info module is disabled, ignore expensive neighbor info packets
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
p->decoded.portnum == meshtastic_PortNum_NEIGHBORINFO_APP &&
(!moduleConfig.has_neighbor_info || !moduleConfig.neighbor_info.enabled)) {
LOG_DEBUG("Neighbor info module is disabled, ignoring neighbor packet\n");
cancelSending(p->from, p->id);
skipHandle = true;
}
#if !MESHTASTIC_EXCLUDE_MQTT
// Publish received message to MQTT if we're not the original transmitter of the packet
if (moduleConfig.mqtt.enabled && getFrom(p) != nodeDB.getNodeNum() && mqtt)
if (!skipHandle && moduleConfig.mqtt.enabled && getFrom(p) != nodeDB->getNodeNum() && mqtt)
mqtt->onSend(*p_encrypted, *p, p->channel);
#endif
} else {
printPacket("packet decoding failed or skipped (no PSK?)", p);
}
@@ -461,7 +479,8 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
packetPool.release(p_encrypted); // Release the encrypted packet
// call modules here
MeshModule::callPlugins(*p, src);
if (!skipHandle)
MeshModule::callPlugins(*p, src);
}
void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
+2 -1
View File
@@ -104,7 +104,8 @@ class Router : protected concurrency::OSThread
/**
* Send an ack or a nak packet back towards whoever sent idFrom
*/
void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex, uint8_t hopStart = 0,
uint8_t hopLimit = 0);
private:
/**
+1 -1
View File
@@ -17,7 +17,7 @@ SX126xInterface<T>::SX126xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs
RADIOLIB_PIN_TYPE busy)
: RadioLibInterface(hal, cs, irq, rst, busy, &lora), lora(&module)
{
LOG_WARN("SX126xInterface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy);
LOG_DEBUG("SX126xInterface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy);
}
/// Initialise the Driver transport hardware and software.
+6 -6
View File
@@ -17,7 +17,7 @@ SX128xInterface<T>::SX128xInterface(LockingArduinoHal *hal, RADIOLIB_PIN_TYPE cs
RADIOLIB_PIN_TYPE busy)
: RadioLibInterface(hal, cs, irq, rst, busy, &lora), lora(&module)
{
LOG_WARN("SX128xInterface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy);
LOG_DEBUG("SX128xInterface(cs=%d, irq=%d, rst=%d, busy=%d)\n", cs, irq, rst, busy);
}
/// Initialise the Driver transport hardware and software.
@@ -71,7 +71,7 @@ template <typename T> bool SX128xInterface<T>::init()
if ((config.lora.region != meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (res == RADIOLIB_ERR_INVALID_FREQUENCY)) {
LOG_WARN("Radio chip only supports 2.4GHz LoRa. Adjusting Region and rebooting.\n");
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_LORA_24;
nodeDB.saveToDisk(SEGMENT_CONFIG);
nodeDB->saveToDisk(SEGMENT_CONFIG);
delay(2000);
#if defined(ARCH_ESP32)
ESP.restart();
@@ -251,9 +251,9 @@ template <typename T> void SX128xInterface<T>::startReceive()
#endif
// We use the PREAMBLE_DETECTED and HEADER_VALID IRQ flag to detect whether we are actively receiving
int err = lora.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_SX128X_IRQ_RX_DEFAULT |
RADIOLIB_SX128X_IRQ_PREAMBLE_DETECTED |
RADIOLIB_SX128X_IRQ_HEADER_VALID);
int err =
lora.startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_SX128X_IRQ_RX_DEFAULT | RADIOLIB_SX128X_IRQ_PREAMBLE_DETECTED |
RADIOLIB_SX128X_IRQ_HEADER_VALID);
assert(err == RADIOLIB_ERR_NONE);
@@ -327,4 +327,4 @@ template <typename T> bool SX128xInterface<T>::sleep()
#endif
return true;
}
}
+3
View File
@@ -10,6 +10,9 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo
info.snr = lite->snr;
info.last_heard = lite->last_heard;
info.channel = lite->channel;
info.via_mqtt = lite->via_mqtt;
info.hops_away = lite->hops_away;
info.is_favorite = lite->is_favorite;
if (lite->has_position) {
info.has_position = true;
+5 -1
View File
@@ -2,9 +2,12 @@
#include "NodeDB.h"
#include "RTC.h"
#include "concurrency/Periodic.h"
#include "configuration.h"
#include "main.h"
#include "mesh/api/ethServerAPI.h"
#if !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h"
#endif
#include "target_specific.h"
#include <RAK13800_W5100S.h>
#include <SPI.h>
@@ -66,11 +69,12 @@ static int32_t reconnectETH()
ethStartupComplete = true;
}
#if !MESHTASTIC_EXCLUDE_MQTT
// FIXME this is kinda yucky, instead we should just have an observable for 'wifireconnected'
if (mqtt && !moduleConfig.mqtt.proxy_to_client_enabled && !mqtt->isConnectedDirectly()) {
mqtt->reconnect();
}
#endif
}
#ifndef DISABLE_NTP
+17 -1
View File
@@ -7,7 +7,6 @@
#include "meshtastic/channel.pb.h"
#include "meshtastic/config.pb.h"
#include "meshtastic/connection_status.pb.h"
#include "meshtastic/deviceonly.pb.h"
#include "meshtastic/mesh.pb.h"
#include "meshtastic/module_config.pb.h"
@@ -154,6 +153,14 @@ typedef struct _meshtastic_AdminMessage {
char set_ringtone_message[231];
/* Remove the node by the specified node-num from the NodeDB on the device */
uint32_t remove_by_nodenum;
/* Set specified node-num to be favorited on the NodeDB on the device */
uint32_t set_favorite_node;
/* Set specified node-num to be un-favorited on the NodeDB on the device */
uint32_t remove_favorite_node;
/* Set fixed position data on the node and then set the position.fixed_position = true */
meshtastic_Position set_fixed_position;
/* Clear fixed position coordinates and then set position.fixed_position = false */
bool remove_fixed_position;
/* Begins an edit transaction for config, module config, owner, and channel settings changes
This will delay the standard *implicit* save to the file system and subsequent reboot behavior until committed (commit_edit_settings) */
bool begin_edit_settings;
@@ -238,6 +245,10 @@ extern "C" {
#define meshtastic_AdminMessage_set_canned_message_module_messages_tag 36
#define meshtastic_AdminMessage_set_ringtone_message_tag 37
#define meshtastic_AdminMessage_remove_by_nodenum_tag 38
#define meshtastic_AdminMessage_set_favorite_node_tag 39
#define meshtastic_AdminMessage_remove_favorite_node_tag 40
#define meshtastic_AdminMessage_set_fixed_position_tag 41
#define meshtastic_AdminMessage_remove_fixed_position_tag 42
#define meshtastic_AdminMessage_begin_edit_settings_tag 64
#define meshtastic_AdminMessage_commit_edit_settings_tag 65
#define meshtastic_AdminMessage_reboot_ota_seconds_tag 95
@@ -277,6 +288,10 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_module_config,set_module
X(a, STATIC, ONEOF, STRING, (payload_variant,set_canned_message_module_messages,set_canned_message_module_messages), 36) \
X(a, STATIC, ONEOF, STRING, (payload_variant,set_ringtone_message,set_ringtone_message), 37) \
X(a, STATIC, ONEOF, UINT32, (payload_variant,remove_by_nodenum,remove_by_nodenum), 38) \
X(a, STATIC, ONEOF, UINT32, (payload_variant,set_favorite_node,set_favorite_node), 39) \
X(a, STATIC, ONEOF, UINT32, (payload_variant,remove_favorite_node,remove_favorite_node), 40) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed_position), 41) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \
X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \
@@ -299,6 +314,7 @@ X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset),
#define meshtastic_AdminMessage_payload_variant_set_channel_MSGTYPE meshtastic_Channel
#define meshtastic_AdminMessage_payload_variant_set_config_MSGTYPE meshtastic_Config
#define meshtastic_AdminMessage_payload_variant_set_module_config_MSGTYPE meshtastic_ModuleConfig
#define meshtastic_AdminMessage_payload_variant_set_fixed_position_MSGTYPE meshtastic_Position
#define meshtastic_HamParameters_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, call_sign, 1) \
@@ -6,7 +6,7 @@
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(meshtastic_DeviceState, meshtastic_DeviceState, 4)
PB_BIND(meshtastic_DeviceState, meshtastic_DeviceState, 2)
PB_BIND(meshtastic_NodeInfoLite, meshtastic_NodeInfoLite, AUTO)
@@ -21,8 +21,5 @@ PB_BIND(meshtastic_ChannelFile, meshtastic_ChannelFile, 2)
PB_BIND(meshtastic_OEMStore, meshtastic_OEMStore, 2)
PB_BIND(meshtastic_NodeRemoteHardwarePin, meshtastic_NodeRemoteHardwarePin, AUTO)
+90 -98
View File
@@ -4,6 +4,7 @@
#ifndef PB_MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_INCLUDED
#include <pb.h>
#include <vector>
#include "meshtastic/channel.pb.h"
#include "meshtastic/localonly.pb.h"
#include "meshtastic/mesh.pb.h"
@@ -14,18 +15,6 @@
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Enum definitions */
/* TODO: REPLACE */
typedef enum _meshtastic_ScreenFonts {
/* TODO: REPLACE */
meshtastic_ScreenFonts_FONT_SMALL = 0,
/* TODO: REPLACE */
meshtastic_ScreenFonts_FONT_MEDIUM = 1,
/* TODO: REPLACE */
meshtastic_ScreenFonts_FONT_LARGE = 2
} meshtastic_ScreenFonts;
/* Struct definitions */
/* Position with static location information only for NodeDBLite */
typedef struct _meshtastic_PositionLite {
/* The new preferred location encoding, multiply by 1e-7 to get degrees
@@ -65,8 +54,70 @@ typedef struct _meshtastic_NodeInfoLite {
meshtastic_DeviceMetrics device_metrics;
/* local channel index we heard that node on. Only populated if its not the default channel. */
uint8_t channel;
/* True if we witnessed the node over MQTT instead of LoRA transport */
bool via_mqtt;
/* Number of hops away from us this node is (0 if adjacent) */
uint8_t hops_away;
/* True if node is in our favorites list
Persists between NodeDB internal clean ups */
bool is_favorite;
} meshtastic_NodeInfoLite;
/* Enum definitions */
/* TODO: REPLACE */
typedef enum _meshtastic_ScreenFonts {
/* TODO: REPLACE */
meshtastic_ScreenFonts_FONT_SMALL = 0,
/* TODO: REPLACE */
meshtastic_ScreenFonts_FONT_MEDIUM = 1,
/* TODO: REPLACE */
meshtastic_ScreenFonts_FONT_LARGE = 2
} meshtastic_ScreenFonts;
/* Struct definitions */
/* This message is never sent over the wire, but it is used for serializing DB
state to flash in the device code
FIXME, since we write this each time we enter deep sleep (and have infinite
flash) it would be better to use some sort of append only data structure for
the receive queue and use the preferences store for the other stuff */
typedef struct _meshtastic_DeviceState {
/* Read only settings/info about this node */
bool has_my_node;
meshtastic_MyNodeInfo my_node;
/* My owner info */
bool has_owner;
meshtastic_User owner;
/* Received packets saved for delivery to the phone */
pb_size_t receive_queue_count;
meshtastic_MeshPacket receive_queue[1];
/* We keep the last received text message (only) stored in the device flash,
so we can show it on the screen.
Might be null */
bool has_rx_text_message;
meshtastic_MeshPacket rx_text_message;
/* A version integer used to invalidate old save files when we make
incompatible changes This integer is set at build time and is private to
NodeDB.cpp in the device code. */
uint32_t version;
/* Used only during development.
Indicates developer is testing and changes should never be saved to flash.
Deprecated in 2.3.1 */
bool no_save;
/* Some GPS receivers seem to have bogus settings from the factory, so we always do one factory reset. */
bool did_gps_reset;
/* We keep the last received waypoint stored in the device flash,
so we can show it on the screen.
Might be null */
bool has_rx_waypoint;
meshtastic_MeshPacket rx_waypoint;
/* The mesh's nodes with their available gpio pins for RemoteHardware module */
pb_size_t node_remote_hardware_pins_count;
meshtastic_NodeRemoteHardwarePin node_remote_hardware_pins[12];
/* New lite version of NodeDB to decrease memory footprint */
std::vector<meshtastic_NodeInfoLite> node_db_lite;
} meshtastic_DeviceState;
/* The on-disk saved channels */
typedef struct _meshtastic_ChannelFile {
/* The channels our node knows about */
@@ -103,57 +154,6 @@ typedef struct _meshtastic_OEMStore {
meshtastic_LocalModuleConfig oem_local_module_config;
} meshtastic_OEMStore;
/* RemoteHardwarePins associated with a node */
typedef struct _meshtastic_NodeRemoteHardwarePin {
/* The node_num exposing the available gpio pin */
uint32_t node_num;
/* The the available gpio pin for usage with RemoteHardware module */
bool has_pin;
meshtastic_RemoteHardwarePin pin;
} meshtastic_NodeRemoteHardwarePin;
/* This message is never sent over the wire, but it is used for serializing DB
state to flash in the device code
FIXME, since we write this each time we enter deep sleep (and have infinite
flash) it would be better to use some sort of append only data structure for
the receive queue and use the preferences store for the other stuff */
typedef struct _meshtastic_DeviceState {
/* Read only settings/info about this node */
bool has_my_node;
meshtastic_MyNodeInfo my_node;
/* My owner info */
bool has_owner;
meshtastic_User owner;
/* Received packets saved for delivery to the phone */
pb_size_t receive_queue_count;
meshtastic_MeshPacket receive_queue[1];
/* We keep the last received text message (only) stored in the device flash,
so we can show it on the screen.
Might be null */
bool has_rx_text_message;
meshtastic_MeshPacket rx_text_message;
/* A version integer used to invalidate old save files when we make
incompatible changes This integer is set at build time and is private to
NodeDB.cpp in the device code. */
uint32_t version;
/* Used only during development.
Indicates developer is testing and changes should never be saved to flash. */
bool no_save;
/* Some GPS receivers seem to have bogus settings from the factory, so we always do one factory reset. */
bool did_gps_reset;
/* We keep the last received waypoint stored in the device flash,
so we can show it on the screen.
Might be null */
bool has_rx_waypoint;
meshtastic_MeshPacket rx_waypoint;
/* The mesh's nodes with their available gpio pins for RemoteHardware module */
pb_size_t node_remote_hardware_pins_count;
meshtastic_NodeRemoteHardwarePin node_remote_hardware_pins[12];
/* New lite version of NodeDB to decrease memory footprint */
pb_size_t node_db_lite_count;
meshtastic_NodeInfoLite node_db_lite[100];
} meshtastic_DeviceState;
#ifdef __cplusplus
extern "C" {
@@ -172,22 +172,29 @@ extern "C" {
#define meshtastic_OEMStore_oem_font_ENUMTYPE meshtastic_ScreenFonts
/* Initializer values for message structs */
#define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}, 0, {meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default, meshtastic_NodeInfoLite_init_default}}
#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_User_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0}
#define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}, {{NULL}, NULL}}
#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_User_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0}
#define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN}
#define meshtastic_ChannelFile_init_default {0, {meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default}, 0}
#define meshtastic_OEMStore_init_default {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_default, false, meshtastic_LocalModuleConfig_init_default}
#define meshtastic_NodeRemoteHardwarePin_init_default {0, false, meshtastic_RemoteHardwarePin_init_default}
#define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}, 0, {meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero, meshtastic_NodeInfoLite_init_zero}}
#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0}
#define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}, {{NULL}, NULL}}
#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0}
#define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN}
#define meshtastic_ChannelFile_init_zero {0, {meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero}, 0}
#define meshtastic_OEMStore_init_zero {0, 0, {0, {0}}, _meshtastic_ScreenFonts_MIN, "", {0, {0}}, false, meshtastic_LocalConfig_init_zero, false, meshtastic_LocalModuleConfig_init_zero}
#define meshtastic_NodeRemoteHardwarePin_init_zero {0, false, meshtastic_RemoteHardwarePin_init_zero}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_DeviceState_my_node_tag 2
#define meshtastic_DeviceState_owner_tag 3
#define meshtastic_DeviceState_receive_queue_tag 5
#define meshtastic_DeviceState_rx_text_message_tag 7
#define meshtastic_DeviceState_version_tag 8
#define meshtastic_DeviceState_no_save_tag 9
#define meshtastic_DeviceState_did_gps_reset_tag 11
#define meshtastic_DeviceState_rx_waypoint_tag 12
#define meshtastic_DeviceState_node_remote_hardware_pins_tag 13
#define meshtastic_DeviceState_node_db_lite_tag 14
#define meshtastic_PositionLite_latitude_i_tag 1
#define meshtastic_PositionLite_longitude_i_tag 2
#define meshtastic_PositionLite_altitude_tag 3
@@ -200,6 +207,9 @@ extern "C" {
#define meshtastic_NodeInfoLite_last_heard_tag 5
#define meshtastic_NodeInfoLite_device_metrics_tag 6
#define meshtastic_NodeInfoLite_channel_tag 7
#define meshtastic_NodeInfoLite_via_mqtt_tag 8
#define meshtastic_NodeInfoLite_hops_away_tag 9
#define meshtastic_NodeInfoLite_is_favorite_tag 10
#define meshtastic_ChannelFile_channels_tag 1
#define meshtastic_ChannelFile_version_tag 2
#define meshtastic_OEMStore_oem_icon_width_tag 1
@@ -210,18 +220,6 @@ extern "C" {
#define meshtastic_OEMStore_oem_aes_key_tag 6
#define meshtastic_OEMStore_oem_local_config_tag 7
#define meshtastic_OEMStore_oem_local_module_config_tag 8
#define meshtastic_NodeRemoteHardwarePin_node_num_tag 1
#define meshtastic_NodeRemoteHardwarePin_pin_tag 2
#define meshtastic_DeviceState_my_node_tag 2
#define meshtastic_DeviceState_owner_tag 3
#define meshtastic_DeviceState_receive_queue_tag 5
#define meshtastic_DeviceState_rx_text_message_tag 7
#define meshtastic_DeviceState_version_tag 8
#define meshtastic_DeviceState_no_save_tag 9
#define meshtastic_DeviceState_did_gps_reset_tag 11
#define meshtastic_DeviceState_rx_waypoint_tag 12
#define meshtastic_DeviceState_node_remote_hardware_pins_tag 13
#define meshtastic_DeviceState_node_db_lite_tag 14
/* Struct field encoding specification for nanopb */
#define meshtastic_DeviceState_FIELDLIST(X, a) \
@@ -234,8 +232,9 @@ X(a, STATIC, SINGULAR, BOOL, no_save, 9) \
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11) \
X(a, STATIC, OPTIONAL, MESSAGE, rx_waypoint, 12) \
X(a, STATIC, REPEATED, MESSAGE, node_remote_hardware_pins, 13) \
X(a, STATIC, REPEATED, MESSAGE, node_db_lite, 14)
#define meshtastic_DeviceState_CALLBACK NULL
X(a, CALLBACK, REPEATED, MESSAGE, node_db_lite, 14)
extern bool meshtastic_DeviceState_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field);
#define meshtastic_DeviceState_CALLBACK meshtastic_DeviceState_callback
#define meshtastic_DeviceState_DEFAULT NULL
#define meshtastic_DeviceState_my_node_MSGTYPE meshtastic_MyNodeInfo
#define meshtastic_DeviceState_owner_MSGTYPE meshtastic_User
@@ -252,7 +251,10 @@ X(a, STATIC, OPTIONAL, MESSAGE, position, 3) \
X(a, STATIC, SINGULAR, FLOAT, snr, 4) \
X(a, STATIC, SINGULAR, FIXED32, last_heard, 5) \
X(a, STATIC, OPTIONAL, MESSAGE, device_metrics, 6) \
X(a, STATIC, SINGULAR, UINT32, channel, 7)
X(a, STATIC, SINGULAR, UINT32, channel, 7) \
X(a, STATIC, SINGULAR, BOOL, via_mqtt, 8) \
X(a, STATIC, SINGULAR, UINT32, hops_away, 9) \
X(a, STATIC, SINGULAR, BOOL, is_favorite, 10)
#define meshtastic_NodeInfoLite_CALLBACK NULL
#define meshtastic_NodeInfoLite_DEFAULT NULL
#define meshtastic_NodeInfoLite_user_MSGTYPE meshtastic_User
@@ -289,19 +291,11 @@ X(a, STATIC, OPTIONAL, MESSAGE, oem_local_module_config, 8)
#define meshtastic_OEMStore_oem_local_config_MSGTYPE meshtastic_LocalConfig
#define meshtastic_OEMStore_oem_local_module_config_MSGTYPE meshtastic_LocalModuleConfig
#define meshtastic_NodeRemoteHardwarePin_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, node_num, 1) \
X(a, STATIC, OPTIONAL, MESSAGE, pin, 2)
#define meshtastic_NodeRemoteHardwarePin_CALLBACK NULL
#define meshtastic_NodeRemoteHardwarePin_DEFAULT NULL
#define meshtastic_NodeRemoteHardwarePin_pin_MSGTYPE meshtastic_RemoteHardwarePin
extern const pb_msgdesc_t meshtastic_DeviceState_msg;
extern const pb_msgdesc_t meshtastic_NodeInfoLite_msg;
extern const pb_msgdesc_t meshtastic_PositionLite_msg;
extern const pb_msgdesc_t meshtastic_ChannelFile_msg;
extern const pb_msgdesc_t meshtastic_OEMStore_msg;
extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePin_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define meshtastic_DeviceState_fields &meshtastic_DeviceState_msg
@@ -309,14 +303,12 @@ extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePin_msg;
#define meshtastic_PositionLite_fields &meshtastic_PositionLite_msg
#define meshtastic_ChannelFile_fields &meshtastic_ChannelFile_msg
#define meshtastic_OEMStore_fields &meshtastic_OEMStore_msg
#define meshtastic_NodeRemoteHardwarePin_fields &meshtastic_NodeRemoteHardwarePin_msg
/* Maximum encoded size of messages (where known) */
/* meshtastic_DeviceState_size depends on runtime parameters */
#define meshtastic_ChannelFile_size 702
#define meshtastic_DeviceState_size 17062
#define meshtastic_NodeInfoLite_size 153
#define meshtastic_NodeRemoteHardwarePin_size 29
#define meshtastic_OEMStore_size 3246
#define meshtastic_NodeInfoLite_size 160
#define meshtastic_OEMStore_size 3278
#define meshtastic_PositionLite_size 28
#ifdef __cplusplus
+1 -1
View File
@@ -181,7 +181,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
/* Maximum encoded size of messages (where known) */
#define meshtastic_LocalConfig_size 469
#define meshtastic_LocalModuleConfig_size 631
#define meshtastic_LocalModuleConfig_size 663
#ifdef __cplusplus
} /* extern "C" */
@@ -60,6 +60,12 @@ PB_BIND(meshtastic_Neighbor, meshtastic_Neighbor, AUTO)
PB_BIND(meshtastic_DeviceMetadata, meshtastic_DeviceMetadata, AUTO)
PB_BIND(meshtastic_Heartbeat, meshtastic_Heartbeat, AUTO)
PB_BIND(meshtastic_NodeRemoteHardwarePin, meshtastic_NodeRemoteHardwarePin, AUTO)
+105 -42
View File
@@ -534,8 +534,7 @@ typedef struct _meshtastic_MeshPacket {
Note: Our crypto implementation uses this field as well.
See [crypto](/docs/overview/encryption) for details. */
uint32_t from;
/* The (immediatSee Priority description for more details.y should be fixed32 instead, this encoding only
hurts the ble link though. */
/* The (immediate) destination for this packet */
uint32_t to;
/* (Usually) If set, this indicates the index in the secondary_channels table that this packet was sent/received on.
If unset, packet was on the primary channel.
@@ -594,6 +593,9 @@ typedef struct _meshtastic_MeshPacket {
meshtastic_MeshPacket_Delayed delayed;
/* Describes whether this packet passed via MQTT somewhere along the path it currently took. */
bool via_mqtt;
/* Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header.
When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled. */
uint8_t hop_start;
} meshtastic_MeshPacket;
/* The bluetooth to device link:
@@ -632,6 +634,13 @@ typedef struct _meshtastic_NodeInfo {
meshtastic_DeviceMetrics device_metrics;
/* local channel index we heard that node on. Only populated if its not the default channel. */
uint8_t channel;
/* True if we witnessed the node over MQTT instead of LoRA transport */
bool via_mqtt;
/* Number of hops away from us this node is (0 if adjacent) */
uint8_t hops_away;
/* True if node is in our favorites list
Persists between NodeDB internal clean ups */
bool is_favorite;
} meshtastic_NodeInfo;
/* Unique local debugging info for this node
@@ -676,32 +685,6 @@ typedef struct _meshtastic_QueueStatus {
uint32_t mesh_packet_id;
} meshtastic_QueueStatus;
/* Packets/commands to the radio will be written (reliably) to the toRadio characteristic.
Once the write completes the phone can assume it is handled. */
typedef struct _meshtastic_ToRadio {
pb_size_t which_payload_variant;
union {
/* Send this packet on the mesh */
meshtastic_MeshPacket packet;
/* Phone wants radio to send full node db to the phone, This is
typically the first packet sent to the radio when the phone gets a
bluetooth connection. The radio will respond by sending back a
MyNodeInfo, a owner, a radio config and a series of
FromRadio.node_infos, and config_complete
the integer you write into this field will be reported back in the
config_complete_id response this allows clients to never be confused by
a stale old partially sent config. */
uint32_t want_config_id;
/* Tell API server we are disconnecting now.
This is useful for serial links where there is no hardware/protocol based notification that the client has dropped the link.
(Sending this message is optional for clients) */
bool disconnect;
meshtastic_XModem xmodemPacket;
/* MQTT Client Proxy Message (for client / phone subscribed to MQTT sending to device) */
meshtastic_MqttClientProxyMessage mqttClientProxyMessage;
};
} meshtastic_ToRadio;
typedef PB_BYTES_ARRAY_T(237) meshtastic_Compressed_data_t;
/* Compressed message payload */
typedef struct _meshtastic_Compressed {
@@ -809,6 +792,49 @@ typedef struct _meshtastic_FromRadio {
};
} meshtastic_FromRadio;
/* A heartbeat message is sent to the node from the client to keep the connection alive.
This is currently only needed to keep serial connections alive, but can be used by any PhoneAPI. */
typedef struct _meshtastic_Heartbeat {
char dummy_field;
} meshtastic_Heartbeat;
/* Packets/commands to the radio will be written (reliably) to the toRadio characteristic.
Once the write completes the phone can assume it is handled. */
typedef struct _meshtastic_ToRadio {
pb_size_t which_payload_variant;
union {
/* Send this packet on the mesh */
meshtastic_MeshPacket packet;
/* Phone wants radio to send full node db to the phone, This is
typically the first packet sent to the radio when the phone gets a
bluetooth connection. The radio will respond by sending back a
MyNodeInfo, a owner, a radio config and a series of
FromRadio.node_infos, and config_complete
the integer you write into this field will be reported back in the
config_complete_id response this allows clients to never be confused by
a stale old partially sent config. */
uint32_t want_config_id;
/* Tell API server we are disconnecting now.
This is useful for serial links where there is no hardware/protocol based notification that the client has dropped the link.
(Sending this message is optional for clients) */
bool disconnect;
meshtastic_XModem xmodemPacket;
/* MQTT Client Proxy Message (for client / phone subscribed to MQTT sending to device) */
meshtastic_MqttClientProxyMessage mqttClientProxyMessage;
/* Heartbeat message (used to keep the device connection awake on serial) */
meshtastic_Heartbeat heartbeat;
};
} meshtastic_ToRadio;
/* RemoteHardwarePins associated with a node */
typedef struct _meshtastic_NodeRemoteHardwarePin {
/* The node_num exposing the available gpio pin */
uint32_t node_num;
/* The the available gpio pin for usage with RemoteHardware module */
bool has_pin;
meshtastic_RemoteHardwarePin pin;
} meshtastic_NodeRemoteHardwarePin;
#ifdef __cplusplus
extern "C" {
@@ -882,6 +908,8 @@ extern "C" {
#define meshtastic_DeviceMetadata_hw_model_ENUMTYPE meshtastic_HardwareModel
/* Initializer values for message structs */
#define meshtastic_Position_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_User_init_default {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
@@ -890,8 +918,8 @@ extern "C" {
#define meshtastic_Data_init_default {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
#define meshtastic_Waypoint_init_default {0, 0, 0, 0, 0, "", "", 0}
#define meshtastic_MqttClientProxyMessage_init_default {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0}
#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0}
#define meshtastic_MeshPacket_init_default {0, 0, 0, 0, {meshtastic_Data_init_default}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0}
#define meshtastic_NodeInfo_init_default {0, false, meshtastic_User_init_default, false, meshtastic_Position_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, 0, 0}
#define meshtastic_MyNodeInfo_init_default {0, 0, 0}
#define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_default {0, 0, 0, 0}
@@ -901,6 +929,8 @@ extern "C" {
#define meshtastic_NeighborInfo_init_default {0, 0, 0, 0, {meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default}}
#define meshtastic_Neighbor_init_default {0, 0, 0, 0}
#define meshtastic_DeviceMetadata_init_default {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0}
#define meshtastic_Heartbeat_init_default {0}
#define meshtastic_NodeRemoteHardwarePin_init_default {0, false, meshtastic_RemoteHardwarePin_init_default}
#define meshtastic_Position_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, _meshtastic_Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN}
#define meshtastic_RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
@@ -908,8 +938,8 @@ extern "C" {
#define meshtastic_Data_init_zero {_meshtastic_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
#define meshtastic_Waypoint_init_zero {0, 0, 0, 0, 0, "", "", 0}
#define meshtastic_MqttClientProxyMessage_init_zero {"", 0, {{0, {0}}}, 0}
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0}
#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0}
#define meshtastic_MeshPacket_init_zero {0, 0, 0, 0, {meshtastic_Data_init_zero}, 0, 0, 0, 0, 0, _meshtastic_MeshPacket_Priority_MIN, 0, _meshtastic_MeshPacket_Delayed_MIN, 0, 0}
#define meshtastic_NodeInfo_init_zero {0, false, meshtastic_User_init_zero, false, meshtastic_Position_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, 0, 0}
#define meshtastic_MyNodeInfo_init_zero {0, 0, 0}
#define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_zero {0, 0, 0, 0}
@@ -919,6 +949,8 @@ extern "C" {
#define meshtastic_NeighborInfo_init_zero {0, 0, 0, 0, {meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero}}
#define meshtastic_Neighbor_init_zero {0, 0, 0, 0}
#define meshtastic_DeviceMetadata_init_zero {"", 0, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_Role_MIN, 0, _meshtastic_HardwareModel_MIN, 0}
#define meshtastic_Heartbeat_init_zero {0}
#define meshtastic_NodeRemoteHardwarePin_init_zero {0, false, meshtastic_RemoteHardwarePin_init_zero}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_Position_latitude_i_tag 1
@@ -989,6 +1021,7 @@ extern "C" {
#define meshtastic_MeshPacket_rx_rssi_tag 12
#define meshtastic_MeshPacket_delayed_tag 13
#define meshtastic_MeshPacket_via_mqtt_tag 14
#define meshtastic_MeshPacket_hop_start_tag 15
#define meshtastic_NodeInfo_num_tag 1
#define meshtastic_NodeInfo_user_tag 2
#define meshtastic_NodeInfo_position_tag 3
@@ -996,6 +1029,9 @@ extern "C" {
#define meshtastic_NodeInfo_last_heard_tag 5
#define meshtastic_NodeInfo_device_metrics_tag 6
#define meshtastic_NodeInfo_channel_tag 7
#define meshtastic_NodeInfo_via_mqtt_tag 8
#define meshtastic_NodeInfo_hops_away_tag 9
#define meshtastic_NodeInfo_is_favorite_tag 10
#define meshtastic_MyNodeInfo_my_node_num_tag 1
#define meshtastic_MyNodeInfo_reboot_count_tag 8
#define meshtastic_MyNodeInfo_min_app_version_tag 11
@@ -1007,11 +1043,6 @@ extern "C" {
#define meshtastic_QueueStatus_free_tag 2
#define meshtastic_QueueStatus_maxlen_tag 3
#define meshtastic_QueueStatus_mesh_packet_id_tag 4
#define meshtastic_ToRadio_packet_tag 1
#define meshtastic_ToRadio_want_config_id_tag 3
#define meshtastic_ToRadio_disconnect_tag 4
#define meshtastic_ToRadio_xmodemPacket_tag 5
#define meshtastic_ToRadio_mqttClientProxyMessage_tag 6
#define meshtastic_Compressed_portnum_tag 1
#define meshtastic_Compressed_data_tag 2
#define meshtastic_Neighbor_node_id_tag 1
@@ -1046,6 +1077,14 @@ extern "C" {
#define meshtastic_FromRadio_xmodemPacket_tag 12
#define meshtastic_FromRadio_metadata_tag 13
#define meshtastic_FromRadio_mqttClientProxyMessage_tag 14
#define meshtastic_ToRadio_packet_tag 1
#define meshtastic_ToRadio_want_config_id_tag 3
#define meshtastic_ToRadio_disconnect_tag 4
#define meshtastic_ToRadio_xmodemPacket_tag 5
#define meshtastic_ToRadio_mqttClientProxyMessage_tag 6
#define meshtastic_ToRadio_heartbeat_tag 7
#define meshtastic_NodeRemoteHardwarePin_node_num_tag 1
#define meshtastic_NodeRemoteHardwarePin_pin_tag 2
/* Struct field encoding specification for nanopb */
#define meshtastic_Position_FIELDLIST(X, a) \
@@ -1146,7 +1185,8 @@ X(a, STATIC, SINGULAR, BOOL, want_ack, 10) \
X(a, STATIC, SINGULAR, UENUM, priority, 11) \
X(a, STATIC, SINGULAR, INT32, rx_rssi, 12) \
X(a, STATIC, SINGULAR, UENUM, delayed, 13) \
X(a, STATIC, SINGULAR, BOOL, via_mqtt, 14)
X(a, STATIC, SINGULAR, BOOL, via_mqtt, 14) \
X(a, STATIC, SINGULAR, UINT32, hop_start, 15)
#define meshtastic_MeshPacket_CALLBACK NULL
#define meshtastic_MeshPacket_DEFAULT NULL
#define meshtastic_MeshPacket_payload_variant_decoded_MSGTYPE meshtastic_Data
@@ -1158,7 +1198,10 @@ X(a, STATIC, OPTIONAL, MESSAGE, position, 3) \
X(a, STATIC, SINGULAR, FLOAT, snr, 4) \
X(a, STATIC, SINGULAR, FIXED32, last_heard, 5) \
X(a, STATIC, OPTIONAL, MESSAGE, device_metrics, 6) \
X(a, STATIC, SINGULAR, UINT32, channel, 7)
X(a, STATIC, SINGULAR, UINT32, channel, 7) \
X(a, STATIC, SINGULAR, BOOL, via_mqtt, 8) \
X(a, STATIC, SINGULAR, UINT32, hops_away, 9) \
X(a, STATIC, SINGULAR, BOOL, is_favorite, 10)
#define meshtastic_NodeInfo_CALLBACK NULL
#define meshtastic_NodeInfo_DEFAULT NULL
#define meshtastic_NodeInfo_user_MSGTYPE meshtastic_User
@@ -1222,12 +1265,14 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,packet,packet), 1) \
X(a, STATIC, ONEOF, UINT32, (payload_variant,want_config_id,want_config_id), 3) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,disconnect,disconnect), 4) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 5) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 6)
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 6) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,heartbeat,heartbeat), 7)
#define meshtastic_ToRadio_CALLBACK NULL
#define meshtastic_ToRadio_DEFAULT NULL
#define meshtastic_ToRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket
#define meshtastic_ToRadio_payload_variant_xmodemPacket_MSGTYPE meshtastic_XModem
#define meshtastic_ToRadio_payload_variant_mqttClientProxyMessage_MSGTYPE meshtastic_MqttClientProxyMessage
#define meshtastic_ToRadio_payload_variant_heartbeat_MSGTYPE meshtastic_Heartbeat
#define meshtastic_Compressed_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, portnum, 1) \
@@ -1266,6 +1311,18 @@ X(a, STATIC, SINGULAR, BOOL, hasRemoteHardware, 10)
#define meshtastic_DeviceMetadata_CALLBACK NULL
#define meshtastic_DeviceMetadata_DEFAULT NULL
#define meshtastic_Heartbeat_FIELDLIST(X, a) \
#define meshtastic_Heartbeat_CALLBACK NULL
#define meshtastic_Heartbeat_DEFAULT NULL
#define meshtastic_NodeRemoteHardwarePin_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, node_num, 1) \
X(a, STATIC, OPTIONAL, MESSAGE, pin, 2)
#define meshtastic_NodeRemoteHardwarePin_CALLBACK NULL
#define meshtastic_NodeRemoteHardwarePin_DEFAULT NULL
#define meshtastic_NodeRemoteHardwarePin_pin_MSGTYPE meshtastic_RemoteHardwarePin
extern const pb_msgdesc_t meshtastic_Position_msg;
extern const pb_msgdesc_t meshtastic_User_msg;
extern const pb_msgdesc_t meshtastic_RouteDiscovery_msg;
@@ -1284,6 +1341,8 @@ extern const pb_msgdesc_t meshtastic_Compressed_msg;
extern const pb_msgdesc_t meshtastic_NeighborInfo_msg;
extern const pb_msgdesc_t meshtastic_Neighbor_msg;
extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg;
extern const pb_msgdesc_t meshtastic_Heartbeat_msg;
extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePin_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define meshtastic_Position_fields &meshtastic_Position_msg
@@ -1304,19 +1363,23 @@ extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg;
#define meshtastic_NeighborInfo_fields &meshtastic_NeighborInfo_msg
#define meshtastic_Neighbor_fields &meshtastic_Neighbor_msg
#define meshtastic_DeviceMetadata_fields &meshtastic_DeviceMetadata_msg
#define meshtastic_Heartbeat_fields &meshtastic_Heartbeat_msg
#define meshtastic_NodeRemoteHardwarePin_fields &meshtastic_NodeRemoteHardwarePin_msg
/* Maximum encoded size of messages (where known) */
#define meshtastic_Compressed_size 243
#define meshtastic_Data_size 270
#define meshtastic_DeviceMetadata_size 46
#define meshtastic_FromRadio_size 510
#define meshtastic_Heartbeat_size 0
#define meshtastic_LogRecord_size 81
#define meshtastic_MeshPacket_size 323
#define meshtastic_MeshPacket_size 326
#define meshtastic_MqttClientProxyMessage_size 501
#define meshtastic_MyNodeInfo_size 18
#define meshtastic_NeighborInfo_size 258
#define meshtastic_Neighbor_size 22
#define meshtastic_NodeInfo_size 270
#define meshtastic_NodeInfo_size 277
#define meshtastic_NodeRemoteHardwarePin_size 29
#define meshtastic_Position_size 144
#define meshtastic_QueueStatus_size 23
#define meshtastic_RouteDiscovery_size 40
@@ -6,10 +6,13 @@
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(meshtastic_ModuleConfig, meshtastic_ModuleConfig, AUTO)
PB_BIND(meshtastic_ModuleConfig, meshtastic_ModuleConfig, 2)
PB_BIND(meshtastic_ModuleConfig_MQTTConfig, meshtastic_ModuleConfig_MQTTConfig, AUTO)
PB_BIND(meshtastic_ModuleConfig_MQTTConfig, meshtastic_ModuleConfig_MQTTConfig, 2)
PB_BIND(meshtastic_ModuleConfig_MapReportSettings, meshtastic_ModuleConfig_MapReportSettings, AUTO)
PB_BIND(meshtastic_ModuleConfig_RemoteHardwareConfig, meshtastic_ModuleConfig_RemoteHardwareConfig, AUTO)
@@ -84,6 +84,14 @@ typedef enum _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar {
} meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar;
/* Struct definitions */
/* Settings for reporting unencrypted information about our node to a map via MQTT */
typedef struct _meshtastic_ModuleConfig_MapReportSettings {
/* How often we should report our info to the map (in seconds) */
uint32_t publish_interval_secs;
/* Bits of precision for the location sent (default of 32 is full precision). */
uint32_t position_precision;
} meshtastic_ModuleConfig_MapReportSettings;
/* MQTT Client Config */
typedef struct _meshtastic_ModuleConfig_MQTTConfig {
/* If a meshtastic node is able to reach the internet it will normally attempt to gateway any channels that are marked as
@@ -111,9 +119,14 @@ typedef struct _meshtastic_ModuleConfig_MQTTConfig {
bool tls_enabled;
/* The root topic to use for MQTT messages. Default is "msh".
This is useful if you want to use a single MQTT server for multiple meshtastic networks and separate them via ACLs */
char root[16];
char root[32];
/* If true, we can use the connected phone / client to proxy messages to MQTT instead of a direct connection */
bool proxy_to_client_enabled;
/* If true, we will periodically report unencrypted information about our node to a map via MQTT */
bool map_reporting_enabled;
/* Settings for reporting information about our node to a map via MQTT */
bool has_map_report_settings;
meshtastic_ModuleConfig_MapReportSettings map_report_settings;
} meshtastic_ModuleConfig_MQTTConfig;
/* NeighborInfoModule Config */
@@ -427,6 +440,7 @@ extern "C" {
#define meshtastic_ModuleConfig_AudioConfig_bitrate_ENUMTYPE meshtastic_ModuleConfig_AudioConfig_Audio_Baud
@@ -447,7 +461,8 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_ModuleConfig_init_default {0, {meshtastic_ModuleConfig_MQTTConfig_init_default}}
#define meshtastic_ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0, 0, "", 0}
#define meshtastic_ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0, 0, "", 0, 0, false, meshtastic_ModuleConfig_MapReportSettings_init_default}
#define meshtastic_ModuleConfig_MapReportSettings_init_default {0, 0}
#define meshtastic_ModuleConfig_RemoteHardwareConfig_init_default {0, 0, 0, {meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default, meshtastic_RemoteHardwarePin_init_default}}
#define meshtastic_ModuleConfig_NeighborInfoConfig_init_default {0, 0}
#define meshtastic_ModuleConfig_DetectionSensorConfig_init_default {0, 0, 0, 0, "", 0, 0, 0}
@@ -462,7 +477,8 @@ extern "C" {
#define meshtastic_ModuleConfig_AmbientLightingConfig_init_default {0, 0, 0, 0, 0}
#define meshtastic_RemoteHardwarePin_init_default {0, "", _meshtastic_RemoteHardwarePinType_MIN}
#define meshtastic_ModuleConfig_init_zero {0, {meshtastic_ModuleConfig_MQTTConfig_init_zero}}
#define meshtastic_ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0, 0, "", 0}
#define meshtastic_ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0, 0, "", 0, 0, false, meshtastic_ModuleConfig_MapReportSettings_init_zero}
#define meshtastic_ModuleConfig_MapReportSettings_init_zero {0, 0}
#define meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero {0, 0, 0, {meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero, meshtastic_RemoteHardwarePin_init_zero}}
#define meshtastic_ModuleConfig_NeighborInfoConfig_init_zero {0, 0}
#define meshtastic_ModuleConfig_DetectionSensorConfig_init_zero {0, 0, 0, 0, "", 0, 0, 0}
@@ -478,6 +494,8 @@ extern "C" {
#define meshtastic_RemoteHardwarePin_init_zero {0, "", _meshtastic_RemoteHardwarePinType_MIN}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_ModuleConfig_MapReportSettings_publish_interval_secs_tag 1
#define meshtastic_ModuleConfig_MapReportSettings_position_precision_tag 2
#define meshtastic_ModuleConfig_MQTTConfig_enabled_tag 1
#define meshtastic_ModuleConfig_MQTTConfig_address_tag 2
#define meshtastic_ModuleConfig_MQTTConfig_username_tag 3
@@ -487,6 +505,8 @@ extern "C" {
#define meshtastic_ModuleConfig_MQTTConfig_tls_enabled_tag 7
#define meshtastic_ModuleConfig_MQTTConfig_root_tag 8
#define meshtastic_ModuleConfig_MQTTConfig_proxy_to_client_enabled_tag 9
#define meshtastic_ModuleConfig_MQTTConfig_map_reporting_enabled_tag 10
#define meshtastic_ModuleConfig_MQTTConfig_map_report_settings_tag 11
#define meshtastic_ModuleConfig_NeighborInfoConfig_enabled_tag 1
#define meshtastic_ModuleConfig_NeighborInfoConfig_update_interval_tag 2
#define meshtastic_ModuleConfig_DetectionSensorConfig_enabled_tag 1
@@ -623,9 +643,18 @@ X(a, STATIC, SINGULAR, BOOL, encryption_enabled, 5) \
X(a, STATIC, SINGULAR, BOOL, json_enabled, 6) \
X(a, STATIC, SINGULAR, BOOL, tls_enabled, 7) \
X(a, STATIC, SINGULAR, STRING, root, 8) \
X(a, STATIC, SINGULAR, BOOL, proxy_to_client_enabled, 9)
X(a, STATIC, SINGULAR, BOOL, proxy_to_client_enabled, 9) \
X(a, STATIC, SINGULAR, BOOL, map_reporting_enabled, 10) \
X(a, STATIC, OPTIONAL, MESSAGE, map_report_settings, 11)
#define meshtastic_ModuleConfig_MQTTConfig_CALLBACK NULL
#define meshtastic_ModuleConfig_MQTTConfig_DEFAULT NULL
#define meshtastic_ModuleConfig_MQTTConfig_map_report_settings_MSGTYPE meshtastic_ModuleConfig_MapReportSettings
#define meshtastic_ModuleConfig_MapReportSettings_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, publish_interval_secs, 1) \
X(a, STATIC, SINGULAR, UINT32, position_precision, 2)
#define meshtastic_ModuleConfig_MapReportSettings_CALLBACK NULL
#define meshtastic_ModuleConfig_MapReportSettings_DEFAULT NULL
#define meshtastic_ModuleConfig_RemoteHardwareConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, enabled, 1) \
@@ -764,6 +793,7 @@ X(a, STATIC, SINGULAR, UENUM, type, 3)
extern const pb_msgdesc_t meshtastic_ModuleConfig_msg;
extern const pb_msgdesc_t meshtastic_ModuleConfig_MQTTConfig_msg;
extern const pb_msgdesc_t meshtastic_ModuleConfig_MapReportSettings_msg;
extern const pb_msgdesc_t meshtastic_ModuleConfig_RemoteHardwareConfig_msg;
extern const pb_msgdesc_t meshtastic_ModuleConfig_NeighborInfoConfig_msg;
extern const pb_msgdesc_t meshtastic_ModuleConfig_DetectionSensorConfig_msg;
@@ -781,6 +811,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define meshtastic_ModuleConfig_fields &meshtastic_ModuleConfig_msg
#define meshtastic_ModuleConfig_MQTTConfig_fields &meshtastic_ModuleConfig_MQTTConfig_msg
#define meshtastic_ModuleConfig_MapReportSettings_fields &meshtastic_ModuleConfig_MapReportSettings_msg
#define meshtastic_ModuleConfig_RemoteHardwareConfig_fields &meshtastic_ModuleConfig_RemoteHardwareConfig_msg
#define meshtastic_ModuleConfig_NeighborInfoConfig_fields &meshtastic_ModuleConfig_NeighborInfoConfig_msg
#define meshtastic_ModuleConfig_DetectionSensorConfig_fields &meshtastic_ModuleConfig_DetectionSensorConfig_msg
@@ -801,7 +832,8 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
#define meshtastic_ModuleConfig_CannedMessageConfig_size 49
#define meshtastic_ModuleConfig_DetectionSensorConfig_size 44
#define meshtastic_ModuleConfig_ExternalNotificationConfig_size 42
#define meshtastic_ModuleConfig_MQTTConfig_size 222
#define meshtastic_ModuleConfig_MQTTConfig_size 254
#define meshtastic_ModuleConfig_MapReportSettings_size 12
#define meshtastic_ModuleConfig_NeighborInfoConfig_size 8
#define meshtastic_ModuleConfig_PaxcounterConfig_size 8
#define meshtastic_ModuleConfig_RangeTestConfig_size 10
@@ -809,7 +841,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
#define meshtastic_ModuleConfig_SerialConfig_size 28
#define meshtastic_ModuleConfig_StoreForwardConfig_size 22
#define meshtastic_ModuleConfig_TelemetryConfig_size 36
#define meshtastic_ModuleConfig_size 225
#define meshtastic_ModuleConfig_size 257
#define meshtastic_RemoteHardwarePin_size 21
#ifdef __cplusplus
@@ -9,4 +9,7 @@
PB_BIND(meshtastic_ServiceEnvelope, meshtastic_ServiceEnvelope, AUTO)
PB_BIND(meshtastic_MapReport, meshtastic_MapReport, AUTO)
+68
View File
@@ -5,6 +5,7 @@
#define PB_MESHTASTIC_MESHTASTIC_MQTT_PB_H_INCLUDED
#include <pb.h>
#include "meshtastic/mesh.pb.h"
#include "meshtastic/config.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
@@ -23,6 +24,38 @@ typedef struct _meshtastic_ServiceEnvelope {
char *gateway_id;
} meshtastic_ServiceEnvelope;
/* Information about a node intended to be reported unencrypted to a map using MQTT. */
typedef struct _meshtastic_MapReport {
/* A full name for this user, i.e. "Kevin Hester" */
char long_name[40];
/* A VERY short name, ideally two characters.
Suitable for a tiny OLED screen */
char short_name[5];
/* Role of the node that applies specific settings for a particular use-case */
meshtastic_Config_DeviceConfig_Role role;
/* Hardware model of the node, i.e. T-Beam, Heltec V3, etc... */
meshtastic_HardwareModel hw_model;
/* Device firmware version string */
char firmware_version[18];
/* The region code for the radio (US, CN, EU433, etc...) */
meshtastic_Config_LoRaConfig_RegionCode region;
/* Modem preset used by the radio (LongFast, MediumSlow, etc...) */
meshtastic_Config_LoRaConfig_ModemPreset modem_preset;
/* Whether the node has a channel with default PSK and name (LongFast, MediumSlow, etc...)
and it uses the default frequency slot given the region and modem preset. */
bool has_default_channel;
/* Latitude: multiply by 1e-7 to get degrees in floating point */
int32_t latitude_i;
/* Longitude: multiply by 1e-7 to get degrees in floating point */
int32_t longitude_i;
/* Altitude in meters above MSL */
int32_t altitude;
/* Indicates the bits of precision for latitude and longitude set by the sending node */
uint32_t position_precision;
/* Number of online nodes (heard in the last 2 hours) this node has in its list that were received locally (not via MQTT) */
uint16_t num_online_local_nodes;
} meshtastic_MapReport;
#ifdef __cplusplus
extern "C" {
@@ -30,12 +63,27 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_ServiceEnvelope_init_default {NULL, NULL, NULL}
#define meshtastic_MapReport_init_default {"", "", _meshtastic_Config_DeviceConfig_Role_MIN, _meshtastic_HardwareModel_MIN, "", _meshtastic_Config_LoRaConfig_RegionCode_MIN, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, 0, 0}
#define meshtastic_ServiceEnvelope_init_zero {NULL, NULL, NULL}
#define meshtastic_MapReport_init_zero {"", "", _meshtastic_Config_DeviceConfig_Role_MIN, _meshtastic_HardwareModel_MIN, "", _meshtastic_Config_LoRaConfig_RegionCode_MIN, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, 0, 0}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_ServiceEnvelope_packet_tag 1
#define meshtastic_ServiceEnvelope_channel_id_tag 2
#define meshtastic_ServiceEnvelope_gateway_id_tag 3
#define meshtastic_MapReport_long_name_tag 1
#define meshtastic_MapReport_short_name_tag 2
#define meshtastic_MapReport_role_tag 3
#define meshtastic_MapReport_hw_model_tag 4
#define meshtastic_MapReport_firmware_version_tag 5
#define meshtastic_MapReport_region_tag 6
#define meshtastic_MapReport_modem_preset_tag 7
#define meshtastic_MapReport_has_default_channel_tag 8
#define meshtastic_MapReport_latitude_i_tag 9
#define meshtastic_MapReport_longitude_i_tag 10
#define meshtastic_MapReport_altitude_tag 11
#define meshtastic_MapReport_position_precision_tag 12
#define meshtastic_MapReport_num_online_local_nodes_tag 13
/* Struct field encoding specification for nanopb */
#define meshtastic_ServiceEnvelope_FIELDLIST(X, a) \
@@ -46,13 +94,33 @@ X(a, POINTER, SINGULAR, STRING, gateway_id, 3)
#define meshtastic_ServiceEnvelope_DEFAULT NULL
#define meshtastic_ServiceEnvelope_packet_MSGTYPE meshtastic_MeshPacket
#define meshtastic_MapReport_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, long_name, 1) \
X(a, STATIC, SINGULAR, STRING, short_name, 2) \
X(a, STATIC, SINGULAR, UENUM, role, 3) \
X(a, STATIC, SINGULAR, UENUM, hw_model, 4) \
X(a, STATIC, SINGULAR, STRING, firmware_version, 5) \
X(a, STATIC, SINGULAR, UENUM, region, 6) \
X(a, STATIC, SINGULAR, UENUM, modem_preset, 7) \
X(a, STATIC, SINGULAR, BOOL, has_default_channel, 8) \
X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 9) \
X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 10) \
X(a, STATIC, SINGULAR, INT32, altitude, 11) \
X(a, STATIC, SINGULAR, UINT32, position_precision, 12) \
X(a, STATIC, SINGULAR, UINT32, num_online_local_nodes, 13)
#define meshtastic_MapReport_CALLBACK NULL
#define meshtastic_MapReport_DEFAULT NULL
extern const pb_msgdesc_t meshtastic_ServiceEnvelope_msg;
extern const pb_msgdesc_t meshtastic_MapReport_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define meshtastic_ServiceEnvelope_fields &meshtastic_ServiceEnvelope_msg
#define meshtastic_MapReport_fields &meshtastic_MapReport_msg
/* Maximum encoded size of messages (where known) */
/* meshtastic_ServiceEnvelope_size depends on runtime parameters */
#define meshtastic_MapReport_size 108
#ifdef __cplusplus
} /* extern "C" */
+7 -5
View File
@@ -38,19 +38,19 @@ typedef enum _meshtastic_PortNum {
ENCODING: Protobuf */
meshtastic_PortNum_REMOTE_HARDWARE_APP = 2,
/* The built-in position messaging app.
Payload is a [Position](/docs/developers/protobufs/api#position) message
Payload is a Position message.
ENCODING: Protobuf */
meshtastic_PortNum_POSITION_APP = 3,
/* The built-in user info app.
Payload is a [User](/docs/developers/protobufs/api#user) message
Payload is a User message.
ENCODING: Protobuf */
meshtastic_PortNum_NODEINFO_APP = 4,
/* Protocol control packets for mesh protocol use.
Payload is a [Routing](/docs/developers/protobufs/api#routing) message
Payload is a Routing message.
ENCODING: Protobuf */
meshtastic_PortNum_ROUTING_APP = 5,
/* Admin control packets.
Payload is a [AdminMessage](/docs/developers/protobufs/api#adminmessage) message
Payload is a AdminMessage message.
ENCODING: Protobuf */
meshtastic_PortNum_ADMIN_APP = 6,
/* Compressed TEXT_MESSAGE payloads.
@@ -60,7 +60,7 @@ typedef enum _meshtastic_PortNum {
any incoming TEXT_MESSAGE_COMPRESSED_APP payload and convert to TEXT_MESSAGE_APP. */
meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP = 7,
/* Waypoint payloads.
Payload is a [Waypoint](/docs/developers/protobufs/api#waypoint) message
Payload is a Waypoint message.
ENCODING: Protobuf */
meshtastic_PortNum_WAYPOINT_APP = 8,
/* Audio Payloads.
@@ -122,6 +122,8 @@ typedef enum _meshtastic_PortNum {
/* ATAK Plugin
Portnum for payloads from the official Meshtastic ATAK plugin */
meshtastic_PortNum_ATAK_PLUGIN = 72,
/* Provides unencrypted information about a node for consumption by a map via MQTT */
meshtastic_PortNum_MAP_REPORT_APP = 73,
/* Private applications should use portnums >= 256.
To simplify initial development and testing you can use "PRIVATE_APP"
in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) */

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