Compare commits

...

213 Commits

Author SHA1 Message Date
github-actions[bot]
3bcab0e223 [create-pull-request] automated change (#2768)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2023-09-04 18:38:58 -05:00
GUVWAF
17617ce031 Fix possible memory leak in NeighborInfo Module (#2765)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-09-04 18:38:16 -05:00
Jonathan Bennett
97f0c734e0 Fix Neighborinfo crash
neighbors object wasn't initialized when module disabled, this
initializes it to a safe, empty object.
2023-09-04 18:15:27 -05:00
Jonathan Bennett
e943fffe8c GPS fixes
Work aroung Serial reset issue on ESP32
Don't send unsupported command to G60xx GPS
2023-09-04 15:00:05 -05:00
tropho23
ffcb131171 GPS toggle for RAK4631 (patch 2 of 2) (#2764)
* Added triple-press GPS toggle button changes

* Revert edits to extensions.json

* comma'd

* Update variant.h

Added the line:

// Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press
#define PIN_GPS_EN 34 // GPS power enable pin

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: code8buster <communismisgreat@national.shitposting.agency>
2023-09-04 13:17:01 -05:00
tropho23
bb1fe7cad3 GPS toggle for RAK4631 (patch 1 of 2) (#2763)
* Added triple-press GPS toggle button changes

* Revert edits to extensions.json

* comma'd

* Update platformio.ini

Added line:

-DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: code8buster <communismisgreat@national.shitposting.agency>
2023-09-04 13:16:39 -05:00
Ben Meadors
ad40493a39 Add speed, heading, and DOP to default position flags (#2759)
* Add speed and heading

* Add DOP
2023-09-04 06:46:27 -05:00
Jonathan Bennett
ac62330e1c Found lost byte dropped from moduleinfo
Attempt to fix CFG-GNSS for neo-6m
2023-09-03 22:38:03 -05:00
code8buster
53f6a43661 Merge pull request #2752 from meshtastic/nmea-rate
Adds GPS Serial Speed scanning, fixes ublox GNSS setup errors
2023-09-03 21:57:51 +00:00
Jonathan Bennett
7ad94da1c6 Add more to GPS_DEBUG and fix ubx7 CFG-GNSS 2023-09-03 16:20:01 -05:00
Jonathan Bennett
ecdb75aae0 Correct UBX-CFG-PMS message 2023-09-03 02:16:29 -05:00
Jonathan Bennett
7c98445ca3 Cleanup in prep for commiting
Fix some compilation warnings
gate debug code behind GPS_DEBUG
minor fix in u-blox protocol detection
Begin to gate GPS messages based on protover
2023-09-03 01:50:11 -05:00
Jonathan Bennett
b21368ecfa Add delay and debug code for GPS probe 2023-09-02 20:35:38 -05:00
Jonathan Bennett
1a178c7d33 Add check for GPS Frame Errors message 2023-09-02 04:29:48 -05:00
Jonathan Bennett
5d6f0ea6c4 Fix possible edge case in GPS detection 2023-09-01 16:24:28 -05:00
Ben Meadors
5bd861f3d8 Merge branch 'master' into nmea-rate 2023-09-01 15:08:32 -05:00
GUVWAF
6d93fab495 Add neighbor IDs to MQTT JSON (#2756)
* Add neighbor IDs to JSON

* Limit #neighbors to what we can actually save

* Put neighbor IDs in an array

* Add SNR to neighbors in nested objects
2023-09-01 14:35:57 -05:00
Jonathan Bennett
6803fd7949 More fixes for GPS chips with unexpected baud 2023-09-01 12:11:39 -05:00
Jonathan Bennett
a61f969773 Increase GPS detection timeout slightly 2023-08-31 22:19:50 -05:00
Jonathan Bennett
79cfc4b725 Avoid Serial output mangling with RTOS. 2023-08-31 20:40:01 -05:00
Jonathan Bennett
cf762bbd42 Cut down delay times for GPS probe and init 2023-08-31 20:39:23 -05:00
code8buster
3d2c419d0d Remove leftover debug msg 2023-08-30 16:24:56 -04:00
Jonathan Bennett
903f619609 Add GPS serial speed scan 2023-08-28 19:01:14 -05:00
Jonathan Bennett
2e3f762d3d Catch a nullptr return rather than crash 2023-08-28 04:05:45 -05:00
Jonathan Bennett
a42266f74b GPS: Fix checksum and remove spurious returns 2023-08-28 04:05:45 -05:00
github-actions[bot]
a605c69eb4 [create-pull-request] automated change (#2748)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2023-08-26 07:36:04 -05:00
Thomas Göttgens
282cc0b16a Merge pull request #2737 from meshtastic/BBQ10Kb-Fix
Bbq10 kb fix
2023-08-26 11:22:35 +02:00
Thomas Göttgens
4ab67f3668 IGOR! Fetch me the brain! 2023-08-25 16:44:39 +02:00
Thomas Göttgens
312028b161 Possble fix ESC 2023-08-25 09:27:09 +02:00
Ben Meadors
ecd48db69c Merge branch 'master' into BBQ10Kb-Fix 2023-08-24 19:57:49 -05:00
Ben Meadors
03dc36ea12 Use fixed position regardless of gps lock (#2744) 2023-08-24 10:55:49 -05:00
Mark Trevor Birss
c2ae38405e Update architecture.h (#2746) 2023-08-24 07:58:24 -05:00
Thomas Göttgens
2a1d8c40b4 add TAB and ESC handling 2023-08-24 14:24:26 +02:00
Ben Meadors
e2441c425a Merge branch 'master' into BBQ10Kb-Fix 2023-08-24 07:06:42 -05:00
Mark Trevor Birss
00ffe73ebd Heltec ESP32-C3 HT-CT62 support (#2741)
* Add files via upload

* Update platformio.ini

* Update variant.h

* Update platformio.ini

* Switch to our new HW_MODEL

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Ben Meadors <thebentern@tuta.io>
2023-08-23 07:59:21 -05:00
Thomas Göttgens
3355019de3 Merge branch 'master' into BBQ10Kb-Fix 2023-08-23 09:45:06 +02:00
Thomas Göttgens
5bb207d88b reset sym after second keypress
also remove debug print and non-working scancodes.
2023-08-23 09:44:20 +02:00
github-actions[bot]
5453e4d123 [create-pull-request] automated change (#2742)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2023-08-22 17:11:22 -05:00
Thomas Göttgens
7f1b58a222 trunk fmt 2023-08-22 21:23:37 +02:00
Thomas Göttgens
39357b2686 Merge branch 'BBQ10Kb-Fix' of github.com:meshtastic/firmware into BBQ10Kb-Fix 2023-08-22 21:23:00 +02:00
Thomas Göttgens
d6b629ae04 update SYM Scancode 2023-08-22 21:19:51 +02:00
Thomas Göttgens
7b1aeb60cd Try manual scancode for SYM 2023-08-22 21:19:51 +02:00
Thomas Göttgens
5c7c1cd253 silence compiler warnings 2023-08-22 21:19:51 +02:00
Thomas Göttgens
8cfe130df3 update SYM Scancode 2023-08-22 21:11:19 +02:00
Thomas Göttgens
feef86942d Merge pull request #2738 from meshtastic/neighbourinfo-fix
fix crash and Debug logging in NeighbourInfo
2023-08-22 16:27:52 +02:00
Thomas Göttgens
5f3a8b4924 fix crash and Debug logging in NeighbourInfo 2023-08-22 15:43:21 +02:00
Thomas Göttgens
0fcaaf39b0 Try manual scancode for SYM 2023-08-22 11:23:19 +02:00
Thomas Göttgens
a55eac5c20 silence compiler warnings 2023-08-22 10:58:54 +02:00
github-actions[bot]
b47c9c165a [create-pull-request] automated change (#2733)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-08-19 18:06:33 -05:00
Thomas Göttgens
ecceb10910 different debug print 2023-08-19 21:28:02 +02:00
Thomas Göttgens
6fc76103a0 temporarily Enable debug print 2023-08-19 20:55:50 +02:00
Ben Meadors
f35c7be917 Just putting back DELAYED_INTERVAL for reliability. Ran into problems 2023-08-19 09:45:48 -05:00
Ben Meadors
364364263b Remove range_test goalie from drawing frames 2023-08-19 09:44:40 -05:00
Manuel
ef957bfac5 support BB Q10 keyboard (#2703)
* support BB Q10 keyboard

* remove debug code

* fix wrong logic

* fix left/right keys for cardkb

* Try to enable Q10 kb after all

* cppcheck

* Only fire on Key release and assume 0x0a is a enter as well

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
2023-08-19 08:37:42 -05:00
Ben Meadors
5d78795065 Portnum promiscuity for text messages from other modules (#2732)
* Add interested portnums to TextMessageModule

* Send Detection Sensor Module messages on its own portnum

* Add to Ext. Notification and consolidate logic

* RANGE_TEST_APP portnum for RangeTestModule
2023-08-19 07:46:34 -05:00
github-actions[bot]
2dbdda204f [create-pull-request] automated change (#2731)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2023-08-18 10:17:55 -05:00
Jonathan Bennett
4767bd5497 Rename utils.h to avoid collision with library. (#2730) 2023-08-17 20:22:34 -05:00
Thomas Göttgens
05efd68097 Merge pull request #2729 from meshtastic/trunk-fmt
update trunk to latest version
2023-08-17 13:46:44 +02:00
Thomas Göttgens
a90eef432f update trunk to latest version 2023-08-17 12:17:36 +02:00
Ben Meadors
929b8f6209 Fix thread to use getConfiguredOrDefaultMs (#2727)
* WIP

* Updates

* Move it out of the macro guard so portduino can build

* Changes from feedback

* Use minimum_broadcast_secs as interval if we just broadcasted to avoid wasting cpu cycles

* Fmt

* Merge conflict resolution boogered me up

* Missed a spot

* getConfiguredOrDefaultMs

* Get the minimum interval
2023-08-16 15:08:06 -05:00
Thomas Göttgens
5d76771fab Permanently Enable Canned Messages on T-Deck and Picomputer (#2728)
* - Permanently Enable Canned Messages on T-Deck and Picomputer
- picomputer has a really dark TFT; switch color to white for better UX.

* well, you know... bullock...
2023-08-16 15:07:22 -05:00
Thomas Göttgens
91eb64d7b7 Fix warning about init order (#2725) 2023-08-15 20:17:15 -05:00
Ben Meadors
03fe4c629a Dection Sensor module duty cycle interval optimization (#2723)
* WIP

* Updates

* Move it out of the macro guard so portduino can build

* Changes from feedback

* Use minimum_broadcast_secs as interval if we just broadcasted to avoid wasting cpu cycles

* Fmt

* Merge conflict resolution boogered me up

* Missed a spot
2023-08-15 12:23:07 -05:00
Ben Meadors
144dfe9805 Initial Detection sensor module feature (#2722)
* WIP

* Updates

* Doh!

* Move it out of the macro guard so portduino can build

* Changes from feedback
2023-08-14 19:00:51 -05:00
rcarteraz
18899fd168 Update variant.h (#2719)
remove HAS_GPS 0 so users can add gps

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-08-13 23:45:23 -05:00
code8buster
fcfd83bc89 Remove auto GPS shutoff for fixed position nodes (#2720) 2023-08-13 15:56:49 -05:00
github-actions[bot]
a3d2b6166c [create-pull-request] automated change (#2716)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2023-08-12 20:49:42 -05:00
Manuel
fb5f2e48a5 fix T-Deck trackball crashes (#2714)
* try-fix: nodenum crash during boot

* Revert "try-fix: nodenum crash during boot"

This reverts commit 632012e197.

* fix/workaround: trackball interrupt crashes

* trunk fmt

* add OSThread to trackballInterrupt
2023-08-12 19:44:05 -05:00
Jonathan Bennett
d29c975e3c Comment out extra-chatty debug message (#2715) 2023-08-12 13:51:31 -05:00
Jonathan Bennett
c44986127e More GPS work (#2711)
Increase GPS buffer on esp32
Check for and flush GPS buffer when overfilled and corrupted.

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-08-12 09:29:44 -05:00
Ben Meadors
6e0b6684ee Extend node max to 100 and remove mesh_sds_timeout_secs (#2713)
* Extend node max to 100 and remove mesh_sds_timeout_secs

* Const pointers for you and you and you

* Fixes and supressions

* Missed it

* uint

* Resize

* Derp
2023-08-12 09:29:19 -05:00
Manuel
84ddfea491 try-fix: max nodes crash during boot (#2712)
* try-fix: nodenum crash during boot

* Revert "try-fix: nodenum crash during boot"

This reverts commit 632012e197.

* try-fix: max nodes crash during boot

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-08-12 09:28:58 -05:00
Ben Meadors
3d6fb13f9a Stop failing on low severity styling issues 2023-08-12 06:43:13 -05:00
Manuel
54c48329c8 Merge pull request #2710 from meshtastic/fix-screen-setup
fix repeater crash during screen setup
2023-08-12 10:46:07 +02:00
mverch67
ede31a2080 fix repeater crash 2023-08-11 19:41:21 +02:00
Jonathan Bennett
9470d4694b Pickier parsing of NMEA to detect an L76K chip (#2699)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-08-10 11:21:35 -05:00
github-actions[bot]
2074c76780 [create-pull-request] automated change (#2697)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2023-08-10 05:53:55 -05:00
Thomas Göttgens
9f6584bd67 Merge pull request #2694 from mverch67/t-watch-fixes
T-Watch S3 fixes
2023-08-08 22:49:37 +02:00
mverch67
dd69de9f32 trunk fmt 2023-08-08 20:21:20 +02:00
mverch67
616553ed72 doing the math :) 2023-08-08 20:14:12 +02:00
mverch67
1986267d65 check payload size 2023-08-08 20:02:29 +02:00
mverch67
1d0ac2caf7 trunk fmt 2023-08-08 18:55:00 +02:00
mverch67
746d7268a2 fix picomputer 2023-08-08 18:51:11 +02:00
Manuel
9a7777dbdb Merge branch 'master' into t-watch-fixes 2023-08-08 18:21:57 +02:00
mverch67
0fe99b0caa T-Watch fixes 2023-08-08 18:06:39 +02:00
Ben Meadors
f9798b7dda Bump to 2.2 2023-08-08 08:05:43 -05:00
Ben Meadors
23fb377fd7 Don't cancel sending "seen" messages on MQTT enabled nodes (#2690) 2023-08-08 06:27:26 -05:00
Thomas Göttgens
4808a5c57f also update other boards with those (#2691) 2023-08-07 23:22:17 +00:00
Thomas Göttgens
d25c368985 Try enabling the secondary I2C bus on Heltec V3 (#2689)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-08-07 15:17:26 -05:00
GUVWAF
a5f3dea40b Clarify that when rebooting is not implemented some settings may not apply (#2688)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-08-07 15:16:56 -05:00
Thomas Göttgens
f026c3308c add picomputer 2023-08-07 21:34:01 +02:00
Thomas Göttgens
ed4e7a4cee Change Target Name 2023-08-07 21:33:42 +02:00
Thomas Göttgens
95f67d70ea Merge pull request #2493 from meshtastic/picomputer-s3
Add variant an plumbing for #2468
2023-08-07 21:17:24 +02:00
Thomas Göttgens
e3260c1d19 Merge branch 'master' into picomputer-s3 2023-08-07 20:27:44 +02:00
Thomas Göttgens
98f3be0665 Merge pull request #2647 from meshtastic/2.2-working-changes
2.2 Changes
2023-08-07 20:27:05 +02:00
Ben Meadors
dc31024764 Merge remote-tracking branch 'origin/master' into 2.2-working-changes 2023-08-07 13:24:10 -05:00
Ben Meadors
5bbcb40f7a Updated protos 2023-08-07 13:23:21 -05:00
Thomas Göttgens
8218a729e0 Merge branch 'picomputer-s3' of github.com:meshtastic/firmware into picomputer-s3 2023-08-07 19:58:48 +02:00
Thomas Göttgens
cbc3e605dd fix building for other platforms 2023-08-07 19:57:47 +02:00
Thomas Göttgens
94c41a4fed Merge branch 'master' into picomputer-s3 2023-08-07 19:47:29 +02:00
caveman99
1afe9f75bd [create-pull-request] automated change 2023-08-07 19:46:28 +02:00
Thomas Göttgens
402f8ba524 Let's see what this breaks... 2023-08-07 19:34:42 +02:00
Ben Meadors
04bbdc6b8a Platform / dep updates (#2684)
* Update nordic platform

* Update sensor libs
2023-08-06 10:06:08 -05:00
Thomas Göttgens
0084c0881d Merge branch 'picomputer-s3' of github.com:meshtastic/firmware into picomputer-s3 2023-08-06 16:57:03 +02:00
Thomas Göttgens
8552cc44b9 Merge branch 'master' into picomputer-s3 2023-08-06 16:55:28 +02:00
Thomas Göttgens
684cce7640 trunk fmt 2023-08-06 16:53:37 +02:00
Thomas Göttgens
114eb0c952 enable Canned Messages on T-Deck without presets 2023-08-06 16:53:37 +02:00
Thomas Göttgens
bed2bfa074 trunk fmt 2023-08-06 16:47:55 +02:00
Thomas Göttgens
0aef8703b6 - use LovyanGFX for m5stack
- update some comments
2023-08-06 16:47:55 +02:00
Thomas Göttgens
f5d323fdd3 trunk fmt 2023-08-06 16:21:39 +02:00
Thomas Göttgens
568cc259af Don't crash when no radio detected. 2023-08-06 16:21:39 +02:00
Neil Hao
42039e27e7 Initialize the L76K Chip, use GPS + GLONASS + BEIDOU (#2680)
* 'nano-g2-ultra'

* revert overcommit

* nano-g2-ultra-fmt

* revert overcommit

* revert overcommit

* Added BEIDOU support to L76K

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-08-06 08:28:37 -05:00
Mark Trevor Birss
400b71c152 Update variant.h 2023-08-05 17:42:51 +02:00
Mark Trevor Birss
b1f6ff1280 Update variant.h 2023-08-05 17:42:51 +02:00
Ben Meadors
cfe5c7f31d Tweaking Power FSM states (#2676)
* Tweaking Power FSM states

* Turn bluetooth back on after serial disconnected

* Remove references to deprecated mesh_sds_timeout_secs
2023-08-04 06:01:01 -05:00
Thomas Göttgens
4e54bec525 Force small fonts on Low DPI screens, no matter what the driver default uses. Up till now we assumed large fonts on E-Paper and TFT Screens. (#2677) 2023-08-03 06:58:19 -05:00
Thomas Göttgens
e0bf15b80e trunk fmt 2023-08-03 10:05:38 +02:00
Jm Casler
26264fd908 more fixes for trunk 2023-08-03 10:05:38 +02:00
Jm Casler
794948d7e4 fixing trunk problems 2023-08-03 10:05:38 +02:00
Jm Casler
e9cbe54eca add more documentaiton 2023-08-03 10:05:38 +02:00
Jm Casler
641d117106 Update extensions.json 2023-08-03 10:05:38 +02:00
Jm Casler
5f38e79b8f Add documentation to a few areas 2023-08-03 10:05:38 +02:00
Thomas Göttgens
b238aebe38 Merge branch 'picomputer-s3' of github.com:meshtastic/firmware into picomputer-s3 2023-08-03 09:37:32 +02:00
GUVWAF
e05c8e60d9 Save Neighbors to flash 2023-08-02 20:56:24 +02:00
Ben Meadors
f1bcc300d9 Merge branch 'master' into 2.2-working-changes 2023-08-02 12:58:32 -05:00
Jonathan Bennett
06a6a992c2 GPS Fixes for nrf52 (#2675)
Expands board serial buffer from 64 (!) to 1024
Adds some debugging messages when problems are detected.
2023-08-02 10:08:59 -05:00
Ben Meadors
11be856507 Merge branch 'master' into 2.2-working-changes 2023-08-01 21:00:58 -05:00
github-actions[bot]
7fe815a327 [create-pull-request] automated change (#2674)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2023-08-01 21:00:34 -05:00
Ben Meadors
191a69dd26 Don't create potential NodeInfo storm on telemetry reponse from Repeater (#2673)
* Don't create potential NodeInfo storm on telemetry reponse from Repeaters

* Check decoded
2023-08-01 18:24:40 -05:00
Ben Meadors
9eeec6c083 Reply to Repeater in DeviceTelemetry module (#2661) 2023-08-01 16:18:10 -05:00
Ben Meadors
c4474a7b99 Merge branch 'master' into picomputer-s3 2023-07-31 18:59:50 -05:00
Ben Meadors
0821cff1c8 Merge remote-tracking branch 'origin/master' into 2.2-working-changes 2023-07-31 18:54:53 -05:00
github-actions[bot]
b799b7bf62 [create-pull-request] automated change (#2672)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2023-07-31 18:51:15 -05:00
Ben Meadors
90ec8eae6c Backmerge master protos 2023-07-31 18:28:27 -05:00
Thomas Göttgens
ba172aae32 Merge branch 'picomputer-s3' of github.com:meshtastic/firmware into picomputer-s3 2023-07-31 22:28:04 +02:00
Thomas Göttgens
26338b8f2b Merge branch 'master' into picomputer-s3 2023-07-31 22:27:24 +02:00
Jonathan Bennett
939a359e7e Adds DOP fields to JSON MQTT output (#2671)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-07-31 15:19:36 -05:00
GUVWAF
5a5af4707c SerialModule SIMPLE mode: use write() instead of printf() 2023-07-31 22:18:42 +02:00
Thomas Göttgens
ef5e21d3da Enable Trunk on Windows 2023-07-31 21:37:55 +02:00
Ben Meadors
8a49221b7f Update version.properties 2023-07-30 20:17:57 -05:00
Ben Meadors
76dc805184 Add Nano-g2-ultra 2023-07-30 14:07:17 -05:00
GUVWAF
297708a50b Manually update protos 2023-07-30 17:27:43 +02:00
GUVWAF
a61a4fad3e Merge pull request #2667 from GUVWAF/removeNeighbors
Remove neighbors after twice their broadcast interval
2023-07-30 17:17:17 +02:00
GUVWAF
c66b68b0cc Remove neighbors after twice their broadcast interval 2023-07-30 16:54:39 +02:00
Ben Meadors
97d7a89644 Update protobufs 2023-07-30 07:58:11 -05:00
Ben Meadors
04cba45c60 Merge branch 'master' into 2.2-working-changes 2023-07-30 07:54:11 -05:00
Manuel
502a6596a3 T deck: support keyboard, trackball and touchscreen (#2665)
* add hwid for auto-detection

* fix: heltec-wireless-tracker USB serial

* T-Deck support

* trunk fmt

* set FRAMERATE to 1

* fix some defines

* trunk fmt

* corrected vendor link

* T-Deck: support keyboard, trackball & touch screen

* T-Watch add touchscreen defs, remove getTouch

* fix warnings

* getTouch uint16 -> int16

* fix touch x,y

* fix I2C port

* CannedMsgModule: use entire display height

* trunk fmt

* fix I2C issue for T-Watch

* allow dest selection in canned mode

* fix: allow dest selection in canned mode

* use tft.setBrightness() to poweroff display

* Increased t-watch framerate and added back haptic feedback

* add da ref

* Move to touched

* improved sensitivity and accuracy of touch events

* use double tap to send canned message

* fix warning

* trunk fmt

* Remove extra hapticFeedback()

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-07-30 07:51:26 -05:00
Ben Meadors
5aedd84c7d Merge branch 'master' into 2.2-working-changes 2023-07-29 08:15:17 -05:00
Neil Hao
b9c9f0f865 nano-g2-ultra (#2660)
* 'nano-g2-ultra'

* revert overcommit

* nano-g2-ultra-fmt

* revert overcommit

* revert overcommit

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-07-29 07:54:56 -05:00
GUVWAF
ffcc1a0275 RP2040: Enable ExternalNotification and RangeTest Module, set randomSeed (#2664)
* Enable ExternalNotification (and RangeTest) Module

* Set a random seed at boot
2023-07-29 07:19:58 -05:00
Ben Meadors
3d697f8cf4 Enable SX126X RX Boosted gain by default (#2663) 2023-07-28 10:39:40 -05:00
Ben Meadors
38c9a1ea07 neighborInfo->node_broadcast_interval_secs 2023-07-28 10:36:44 -05:00
Ben Meadors
0eefd0912f Move node_broadcast_interval_secs 2023-07-28 10:28:14 -05:00
Ben Meadors
0cda8e6087 Start plumbing node_broadcast_interval_secs 2023-07-28 06:58:28 -05:00
Ben Meadors
2cf648928a Add node_broadcast_interval_secs to Neighbor 2023-07-28 06:43:21 -05:00
Andre K
3cd7d8d6af update min_app_version to 2.2.0 2023-07-28 06:22:16 -03:00
Ben Meadors
702a83b525 Bumb device state version 2023-07-27 16:03:57 -05:00
GUVWAF
32246850aa Convert protobuf values that are unsigned properly to uint in JSON (#2659) 2023-07-27 13:53:20 -05:00
Ben Meadors
74650ca276 Words are important 2023-07-27 13:23:18 -05:00
Ben Meadors
0141bbe772 Regen 2023-07-27 12:59:06 -05:00
Ben Meadors
049c587ca2 Add phoneapi plumbing. Need to regen protos on my other machine 2023-07-27 12:51:31 -05:00
Ben Meadors
1a28225cd5 Merge branch 'master' into 2.2-working-changes 2023-07-27 08:55:48 -05:00
Ben Meadors
6bd870c454 I guess we have to use SHAs (lame) 2023-07-27 07:59:39 -05:00
Ben Meadors
c782380373 Fix semgrep errors 2023-07-27 07:04:00 -05:00
Ben Meadors
4fd756acd8 Merge branch 'master' into 2.2-working-changes 2023-07-27 06:42:02 -05:00
Ben Meadors
0b509c7e79 Remove concurrency groups for now. They seem to cause CI hangs 2023-07-27 06:41:39 -05:00
Ben Lipsey
86af578df9 Preferred units when distance unknown (#2652)
* units when distance unknown

* replace deleted comment
2023-07-26 18:06:31 -05:00
Thomas Göttgens
ff11506922 Merge pull request #2649 from meshtastic/neighborinfo
add Neighborinfo
2023-07-26 13:07:01 +02:00
Ben Meadors
f35b422365 Trunk 2023-07-25 20:54:01 -05:00
Ben Meadors
08f1ac785a Gut NodeDb and remove deprecated protos 2023-07-25 19:13:19 -05:00
Ben Meadors
146ed067a1 Merge branch 'master' into neighborinfo 2023-07-25 16:14:50 -05:00
Ben Meadors
bdcf17a3f7 Add T-Deck to S3 ota logical branch (#2644)
* Add T-Deck to S3 ota logical branch

* Revert "Add T-Deck to S3 ota logical branch"

This reverts commit d0aef9dc26.

* Add targets

* Get the bat file too
2023-07-25 16:13:32 -05:00
github-actions[bot]
81edf363d7 [create-pull-request] automated change (#2645)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2023-07-25 05:46:11 -05:00
Ben Meadors
96c6a20e03 Ensure that MQTT is enabled and log initialization (#2643) 2023-07-24 12:33:01 -05:00
Ben Meadors
3fbe2d771c Hopefully this cancels previous CI runs for a branch (#2642) 2023-07-24 09:47:16 -05:00
Jonathan Bennett
ac9c81f6d1 Check Position Request for Primary Channel (#2638)
Prevents leaking location data to secondary channels.

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2023-07-24 09:37:56 -05:00
Ben Meadors
2e7c95a110 Merge branch 'master' into neighborinfo 2023-07-24 07:24:34 -05:00
Ben Meadors
490abdac96 Whoops 2023-07-24 07:22:04 -05:00
Ben Meadors
b17436a023 Patch gather-artifacts 2023-07-24 06:54:05 -05:00
rcarteraz
b9ae63cb3c Update Bug Report.yml (#2640)
Add T-Deck, T-Watch, Wireless Paper, and Wireless Tacker to device list.
2023-07-24 06:44:19 -05:00
github-actions[bot]
55701692fd [create-pull-request] automated change (#2637)
Co-authored-by: thebentern <thebentern@users.noreply.github.com>
2023-07-24 06:43:47 -05:00
Ben Meadors
3a24882e76 Merge branch 'master' into neighborinfo 2023-07-22 08:46:27 -05:00
Ben Meadors
41b07de5a2 Merge branch 'master' into neighborinfo 2023-07-17 09:21:08 -05:00
Thomas Göttgens
1745722dac Merge branch 'picomputer-s3' of github.com:meshtastic/firmware into picomputer-s3 2023-06-28 09:12:10 +02:00
Ben Meadors
849599cd8e Merge branch 'master' into picomputer-s3 2023-06-27 15:40:13 -05:00
Thomas Göttgens
bfc567ad89 Add variant an plumbing for #2468 2023-06-27 18:11:58 +02:00
Thomas Göttgens
b665786c77 Merge branch 'master' into neighborinfo 2023-06-27 18:00:20 +02:00
GUVWAF
e60a5f1cf2 Setter for NeighborInfo 2023-06-25 15:41:19 +02:00
GUVWAF
6bdf67c9be Conversion to NodeInfoLite 2023-06-25 15:41:06 +02:00
Ben Meadors
bbfd62c47e Merge branch 'master' into neighborinfo 2023-06-21 21:11:06 -05:00
Ben Meadors
a07e30544d Merge branch 'master' into neighborinfo 2023-06-19 06:16:42 -05:00
Ben Meadors
9716bd8bec Merge branch 'master' into neighborinfo 2023-06-13 05:44:48 -05:00
Ben Meadors
c2168dd450 Merge branch 'master' into neighborinfo 2023-06-09 19:51:22 -05:00
GUVWAF
66c71250b8 Update last_sent_by_id in FloodingRouter 2023-06-08 20:59:09 +02:00
Ben Meadors
5c438ae792 Merge branch 'master' into neighborinfo 2023-06-06 19:29:59 -05:00
GUVWAF
cdf44ce7fa Move module init out of repeater clause 2023-06-06 21:07:12 +02:00
GUVWAF
d70bd23260 Use float print specifier for SNR 2023-06-06 21:06:19 +02:00
Ben Meadors
b42ead4400 Merge branch 'master' into neighborinfo 2023-06-02 06:46:45 -05:00
Ben Meadors
b2704a0082 Merge branch 'master' into neighborinfo 2023-06-01 06:02:42 -05:00
Ben Meadors
9b94701699 Merge branch 'master' into neighborinfo 2023-05-31 20:08:50 -05:00
Thomas Göttgens
61661aed50 Broadcast neighbor info
also update trunk
2023-05-31 13:18:43 +02:00
Thomas Göttgens
3a25d6d3b3 Merge branch 'picomputer-s3' of github.com:meshtastic/firmware into picomputer-s3 2023-05-30 14:52:24 +02:00
Ben Meadors
fd4e9daa7f Merge branch 'master' into picomputer-s3 2023-05-30 07:36:53 -05:00
Thomas Göttgens
b6f7b7fa47 Merge branch 'picomputer-s3' of github.com:meshtastic/firmware into picomputer-s3 2023-05-30 13:07:29 +02:00
Thomas Göttgens
a92a960682 Merge branch 'master' into picomputer-s3 2023-05-30 09:55:33 +02:00
Thomas Göttgens
5d2ab65a81 Merge branch 'picomputer-s3' of github.com:meshtastic/firmware into picomputer-s3 2023-05-27 13:08:57 +02:00
Thomas Göttgens
f3b7f7251c Add variant an plumbing for #2468 2023-05-27 13:08:27 +02:00
Thomas Göttgens
2f60bbe5f2 Merge branch 'picomputer-s3' of github.com:meshtastic/firmware into picomputer-s3 2023-05-24 01:48:44 +02:00
Thomas Göttgens
0261754269 Add variant an plumbing for #2468 2023-05-24 01:48:01 +02:00
Thomas Göttgens
e6fc2af21f Merge branch 'picomputer-s3' of github.com:meshtastic/firmware into picomputer-s3 2023-05-17 20:28:49 +02:00
Ben Meadors
ae41944a89 Merge branch 'master' into picomputer-s3 2023-05-16 10:38:12 -05:00
Thomas Göttgens
62259583e6 Add variant an plumbing for #2468 2023-05-15 17:18:06 +02:00
Thomas Göttgens
c5d87fe581 Add variant an plumbing for #2468 2023-05-15 17:17:14 +02:00
144 changed files with 3824 additions and 780 deletions

View File

@@ -37,7 +37,9 @@ body:
- T-Lora v1
- T-Lora v1.3
- T-Lora v2 1.6
- T-Deck
- T-Echo
- T-Watch
- Rak4631
- Rak11200
- Rak11310
@@ -45,6 +47,8 @@ body:
- Heltec v2
- Heltec v2.1
- Heltec V3
- Heltec Wireless Paper
- Heltec Wireless Tracker
- Raspberry Pi Pico (W)
- Relay v1
- Relay v2

View File

@@ -17,11 +17,11 @@ jobs:
uses: ./.github/actions/setup-base
- name: Pull web ui
uses: dsaltares/fetch-gh-release-asset@master
uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4
with:
repo: "meshtastic/web"
file: "build.tar"
target: "build.tar"
repo: meshtastic/web
file: build.tar
target: build.tar
token: ${{ secrets.GITHUB_TOKEN }}
- name: Unpack web ui
@@ -40,11 +40,11 @@ jobs:
run: bin/build-esp32.sh ${{ inputs.board }}
- name: Pull OTA Firmware
uses: dsaltares/fetch-gh-release-asset@master
uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4
with:
repo: "meshtastic/firmware-ota"
file: "firmware.bin"
target: "release/bleota.bin"
repo: meshtastic/firmware-ota
file: firmware.bin
target: release/bleota.bin
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get release version string

View File

@@ -17,11 +17,11 @@ jobs:
uses: ./.github/actions/setup-base
- name: Pull web ui
uses: dsaltares/fetch-gh-release-asset@master
uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4
with:
repo: "meshtastic/web"
file: "build.tar"
target: "build.tar"
repo: meshtastic/web
file: build.tar
target: build.tar
token: ${{ secrets.GITHUB_TOKEN }}
- name: Unpack web ui
@@ -38,11 +38,11 @@ jobs:
run: bin/build-esp32.sh ${{ inputs.board }}
- name: Pull OTA Firmware
uses: dsaltares/fetch-gh-release-asset@master
uses: dsaltares/fetch-gh-release-asset@a40c8b4a0471f9ab81bdf73a010f74cc51476ad4
with:
repo: "meshtastic/firmware-ota"
file: "firmware-s3.bin"
target: "release/bleota-s3.bin"
repo: meshtastic/firmware-ota
file: firmware-s3.bin
target: release/bleota-s3.bin
token: ${{ secrets.GITHUB_TOKEN }}
- name: Get release version string

View File

@@ -1,4 +1,7 @@
name: CI
#concurrency:
# group: ${{ github.ref }}
# cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
on:
# # Triggers the workflow on push but only for the master branch
push:
@@ -46,7 +49,7 @@ jobs:
- name: Trunk Check
if: ${{ github.event_name != 'workflow_dispatch' }}
uses: trunk-io/trunk-action@v1
uses: trunk-io/trunk-action@782e83f803ca6e369f035d64c6ba2768174ba61b
- name: Check ${{ matrix.board }}
run: bin/check-all.sh ${{ matrix.board }}
@@ -92,6 +95,7 @@ jobs:
- board: tlora-t3s3-v1
- board: t-watch-s3
- board: t-deck
- board: picomputer-s3
uses: ./.github/workflows/build_esp32_s3.yml
with:
board: ${{ matrix.board }}
@@ -108,6 +112,7 @@ jobs:
- board: t-echo
- board: pca10059_diy_eink
- board: feather_diy
- board: nano-g2-ultra
uses: ./.github/workflows/build_nrf52.yml
with:
board: ${{ matrix.board }}
@@ -255,6 +260,7 @@ jobs:
retention-days: 30
- name: Create request artifacts
continue-on-error: true # FIXME: Why are we getting 502, but things still work?
if: ${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request' }}
uses: gavv/pull-request-artifacts@v1.1.0
with:

View File

@@ -14,6 +14,6 @@ jobs:
uses: actions/checkout@v3
- name: Trunk Check
uses: trunk-io/trunk-action@v1
uses: trunk-io/trunk-action@782e83f803ca6e369f035d64c6ba2768174ba61b
with:
trunk-token: ${{ secrets.TRUNK_TOKEN }}

2
.trunk/.gitignore vendored
View File

@@ -5,4 +5,4 @@
plugins
user_trunk.yaml
user.yaml
shims
tools

View File

@@ -1,37 +1,42 @@
version: 0.1
cli:
version: 1.9.1
version: 1.13.0
plugins:
sources:
- id: trunk
ref: v0.0.17
ref: v1.1.1
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- taplo@0.7.0
- ruff@0.0.265
- yamllint@1.31.0
- bandit@1.7.5
- checkov@2.4.1
- terrascan@1.18.3
- trivy@0.44.1
- trufflehog@3.48.0
- taplo@0.8.1
- ruff@0.0.284
- yamllint@1.32.0
- isort@5.12.0
- markdownlint@0.34.0
- markdownlint@0.35.0
- oxipng@8.0.0
- svgo@3.0.2
- actionlint@1.6.24
- flake8@6.0.0
- actionlint@1.6.25
- flake8@6.1.0
- hadolint@2.12.0
- shfmt@3.5.0
- shfmt@3.6.0
- shellcheck@0.9.0
- black@23.3.0
- black@23.7.0
- git-diff-check
- gitleaks@8.16.3
- clang-format@14.0.0
- prettier@2.8.8
- gitleaks@8.17.0
- clang-format@16.0.3
- prettier@3.0.2
disabled:
- taplo@0.7.0
- taplo@0.8.1
- shellcheck@0.9.0
- shfmt@3.5.0
- shfmt@3.6.0
- oxipng@8.0.0
- actionlint@1.6.22
- markdownlint@0.34.0
- markdownlint@0.35.0
- hadolint@2.12.0
- svgo@3.0.2
runtimes:

View File

@@ -6,4 +6,4 @@
"platformio.platformio-ide",
"trunk.io"
],
}
}

View File

@@ -1,4 +1,5 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "trunk.io"
"editor.defaultFormatter": "trunk.io",
"trunk.enableWindows": true
}

View File

@@ -30,6 +30,7 @@ build_flags =
-DCONFIG_BT_NIMBLE_MAX_CCCDS=20
-DCONFIG_BT_NIMBLE_HOST_TASK_STACK_SIZE=5120
-DESP_OPENSSL_SUPPRESS_LEGACY_WARNING
-DSERIAL_BUFFER_SIZE=4096
;-DDEBUG_HEAP
lib_deps =

View File

@@ -1,11 +1,13 @@
[nrf52_base]
; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files
platform = platformio/nordicnrf52@^10.0.0
platform = platformio/nordicnrf52@^10.1.0
extends = arduino_base
build_type = debug ; I'm debugging with ICE a lot now
build_flags =
${arduino_base.build_flags} -Wno-unused-variable
${arduino_base.build_flags}
-DSERIAL_BUFFER_SIZE=1024
-Wno-unused-variable
-Isrc/platform/nrf52
build_src_filter =

View File

@@ -4,23 +4,23 @@
set -e
VERSION=`bin/buildinfo.py long`
VERSION=$(bin/buildinfo.py long)
# The shell vars the build tool expects to find
export APP_VERSION=$VERSION
if [[ $# -gt 0 ]]; then
# can override which environment by passing arg
BOARDS="$@"
# can override which environment by passing arg
BOARDS="$@"
else
BOARDS="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec-v1 heltec-v2.0 heltec-v2.1 tbeam0.7 meshtastic-diy-v1 rak4631 rak4631_eink rak11200 t-echo pca10059_diy_eink"
BOARDS="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec-v1 heltec-v2.0 heltec-v2.1 tbeam0.7 meshtastic-diy-v1 rak4631 rak4631_eink rak11200 t-echo pca10059_diy_eink"
fi
echo "BOARDS:${BOARDS}"
CHECK=""
for BOARD in $BOARDS; do
CHECK="${CHECK} -e ${BOARD}"
CHECK="${CHECK} -e ${BOARD}"
done
pio check --flags "-DAPP_VERSION=${APP_VERSION} --suppressions-list=suppressions.txt" $CHECK --skip-packages --pattern="src/" --fail-on-defect=low --fail-on-defect=medium --fail-on-defect=high
pio check --flags "-DAPP_VERSION=${APP_VERSION} --suppressions-list=suppressions.txt" $CHECK --skip-packages --pattern="src/" --fail-on-defect=medium --fail-on-defect=high

View File

@@ -32,7 +32,7 @@ IF EXIST %FILENAME% IF x%FILENAME:update=%==x%FILENAME% (
%PYTHON% -m esptool --baud 115200 write_flash 0x00 %FILENAME%
@REM Account for S3 board's different OTA partition
IF x%FILENAME:s3=%==x%FILENAME% IF x%FILENAME:v3=%==x%FILENAME% (
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
) else (
%PYTHON% -m esptool --baud 115200 write_flash 0x260000 bleota-s3.bin

View File

@@ -50,7 +50,7 @@ if [ -f "${FILENAME}" ] && [ ! -z "${FILENAME##*"update"*}" ]; then
"$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"*}" ]; then
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
else
"$PYTHON" -m esptool write_flash 0x260000 bleota-s3.bin

View File

@@ -28,8 +28,6 @@
"flash_size": "8MB",
"maximum_ram_size": 327680,
"maximum_size": 8388608,
"use_1200bps_touch": true,
"wait_for_upload_port": true,
"require_upload_port": true,
"speed": 921600
},

51
boards/nano-g2-ultra.json Normal file
View File

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

View File

@@ -15,7 +15,7 @@
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "dio",
"hwids": [["0X303A", "0x1001"]],
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "t-watch-s3"
},

View File

@@ -88,7 +88,7 @@ check_flags =
framework = arduino
lib_deps =
${env.lib_deps}
mprograms/QMC5883LCompass@^1.1.1
mprograms/QMC5883LCompass@^1.2.0
end2endzone/NonBlockingRTTTL@^1.3.0
https://github.com/meshtastic/SparkFun_ATECCX08a_Arduino_Library.git#5cf62b36c6f30bc72a07bdb2c11fc9a22d1e31da
@@ -107,8 +107,8 @@ lib_deps =
[environmental_base]
lib_deps =
adafruit/Adafruit BusIO@^1.11.4
adafruit/Adafruit Unified Sensor@^1.1.9
adafruit/Adafruit BMP280 Library@^2.6.6
adafruit/Adafruit Unified Sensor@^1.1.11
adafruit/Adafruit BMP280 Library@^2.6.8
adafruit/Adafruit BME280 Library@^2.2.2
https://github.com/boschsensortec/Bosch-BSEC2-Library#v1.5.2400
boschsensortec/BME68x Sensor Library@^1.1.40407
@@ -117,7 +117,7 @@ lib_deps =
adafruit/Adafruit INA219@^1.2.0
adafruit/Adafruit SHTC3 Library@^1.0.0
adafruit/Adafruit LPS2X@^2.0.4
adafruit/Adafruit SHT31 Library@^2.2.0
adafruit/Adafruit SHT31 Library@^2.2.2
adafruit/Adafruit PM25 AQI Sensor@^1.0.6
adafruit/Adafruit MPU6050@^2.2.4
adafruit/Adafruit LIS3DH@^1.2.4

View File

@@ -101,23 +101,8 @@ class ButtonThread : public concurrency::OSThread
#endif
// if (!canSleep) LOG_DEBUG("Suppressing sleep!\n");
// else LOG_DEBUG("sleep ok\n");
#if defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
int x, y = 0;
screen->getTouch(&x, &y);
if (x > 0 && y > 0) {
#ifdef T_WATCH_S3
drv.setWaveform(0, 75);
drv.setWaveform(1, 0); // end waveform
drv.go();
#endif
LOG_DEBUG("touch %d %d\n", x, y);
powerFSM.trigger(EVENT_PRESS);
return 150; // Check for next touch every in 150ms
}
#endif
return 5;
return 50;
}
private:

View File

@@ -1,3 +1,13 @@
/**
* @file FSCommon.cpp
* @brief This file contains functions for common filesystem operations such as copying, renaming, listing and deleting files and
* directories.
*
* The functions in this file are used to perform common filesystem operations such as copying, renaming, listing and deleting
* files and directories. These functions are used in the Meshtastic-device project to manage files and directories on the
* device's filesystem.
*
*/
#include "FSCommon.h"
#include "configuration.h"
@@ -14,6 +24,13 @@ SPIClass SPI1(HSPI);
#endif // HAS_SDCARD
/**
* @brief Copies a file from one location to another.
*
* @param from The path of the source file.
* @param to The path of the destination file.
* @return true if the file was successfully copied, false otherwise.
*/
bool copyFile(const char *from, const char *to)
{
#ifdef FSCom
@@ -43,6 +60,14 @@ bool copyFile(const char *from, const char *to)
#endif
}
/**
* Renames a file from pathFrom to pathTo.
*
* @param pathFrom The original path of the file.
* @param pathTo The new path of the file.
*
* @return True if the file was successfully renamed, false otherwise.
*/
bool renameFile(const char *pathFrom, const char *pathTo)
{
#ifdef FSCom
@@ -59,6 +84,13 @@ bool renameFile(const char *pathFrom, const char *pathTo)
#endif
}
/**
* Lists the contents of a directory.
*
* @param dirname The name of the directory to list.
* @param levels The number of levels of subdirectories to list.
* @param del Whether or not to delete the contents of the directory after listing.
*/
void listDir(const char *dirname, uint8_t levels, bool del = false)
{
#ifdef FSCom
@@ -154,6 +186,13 @@ void listDir(const char *dirname, uint8_t levels, bool del = false)
#endif
}
/**
* @brief Removes a directory and all its contents.
*
* This function recursively removes a directory and all its contents, including subdirectories and files.
*
* @param dirname The name of the directory to remove.
*/
void rmDir(const char *dirname)
{
#ifdef FSCom
@@ -182,6 +221,9 @@ void fsInit()
#endif
}
/**
* Initializes the SD card and mounts the file system.
*/
void setupSDCard()
{
#ifdef HAS_SDCARD
@@ -212,4 +254,4 @@ void setupSDCard()
LOG_DEBUG("Total space: %llu MB\n", SD.totalBytes() / (1024 * 1024));
LOG_DEBUG("Used space: %llu MB\n", SD.usedBytes() / (1024 * 1024));
#endif
}
}

View File

@@ -29,6 +29,16 @@ static void IRAM_ATTR onTimer()
(*tCallback)(tParam1, tParam2);
}
/**
* Schedules a hardware callback function to be executed after a specified delay.
*
* @param callback The function to be executed.
* @param param1 The first parameter to be passed to the function.
* @param param2 The second parameter to be passed to the function.
* @param delayMsec The delay time in milliseconds before the function is executed.
*
* @return True if the function was successfully scheduled, false otherwise.
*/
bool scheduleHWCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec)
{
if (!timer) {

View File

@@ -1,11 +1,23 @@
/**
* @file Power.cpp
* @brief This file contains the implementation of the Power class, which is responsible for managing power-related functionality
* of the device. It includes battery level sensing, power management unit (PMU) control, and power state machine management. The
* Power class is used by the main device class to manage power-related functionality.
*
* The file also includes implementations of various battery level sensors, such as the AnalogBatteryLevel class, which assumes
* the battery voltage is attached via a voltage-divider to an analog input.
*
* This file is part of the Meshtastic project.
* For more information, see: https://meshtastic.org/
*/
#include "power.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "buzz/buzz.h"
#include "configuration.h"
#include "main.h"
#include "meshUtils.h"
#include "sleep.h"
#include "utils.h"
#ifdef DEBUG_HEAP_MQTT
#include "mqtt/MQTT.h"
@@ -209,10 +221,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
/**
* return true if there is a battery installed in this unit
*/
virtual bool isBatteryConnect() override
{
return getBatteryPercent() != -1;
}
virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; }
/// If we see a battery voltage higher than physics allows - assume charger is pumping
/// in power
@@ -233,10 +242,7 @@ 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 { return isBatteryConnect() && isVbusIn(); }
private:
/// If we see a battery voltage higher than physics allows - assume charger is pumping
@@ -366,6 +372,11 @@ bool Power::analogInit()
#endif
}
/**
* Initializes the Power class.
*
* @return true if the setup was successful, false otherwise.
*/
bool Power::setup()
{
bool found = axpChipInit();

View File

@@ -1,3 +1,12 @@
/**
* @file PowerFSM.cpp
* @brief Implements the finite state machine for power management.
*
* This file contains the implementation of the finite state machine (FSM) for power management.
* The FSM controls the power states of the device, including SDS (shallow deep sleep), LS (light sleep),
* NB (normal mode), and POWER (powered mode). The FSM also handles transitions between states and
* actions to be taken upon entering or exiting each state.
*/
#include "PowerFSM.h"
#include "GPS.h"
#include "MeshService.h"
@@ -137,7 +146,10 @@ static void nbEnter()
{
LOG_DEBUG("Enter state: NB\n");
screen->setOn(false);
#ifdef ARCH_ESP32
// Only ESP32 should turn off bluetooth
setBluetoothEnable(false);
#endif
// FIXME - check if we already have packets for phone and immediately trigger EVENT_PACKETS_FOR_PHONE
}
@@ -158,12 +170,14 @@ static void serialEnter()
static void serialExit()
{
// Turn bluetooth back on when we leave serial stream API
setBluetoothEnable(true);
screen->print("Serial disconnected\n");
}
static void powerEnter()
{
LOG_DEBUG("Enter state: POWER\n");
// LOG_DEBUG("Enter state: POWER\n");
if (!isPowered()) {
// If we got here, we are in the wrong state - we should be in powered, let that state ahndle things
LOG_INFO("Loss of power in Powered\n");
@@ -242,7 +256,11 @@ void PowerFSM_setup()
// wake timer expired or a packet arrived
// if we are a router node, we go to NB (no need for bluetooth) otherwise we go to DARK (so we can send message to phone)
#ifdef ARCH_ESP32
powerFSM.add_transition(&stateLS, isRouter ? &stateNB : &stateDARK, EVENT_WAKE_TIMER, NULL, "Wake timer");
#else // Don't go into a no-bluetooth state on low power platforms
powerFSM.add_transition(&stateLS, &stateDARK, EVENT_WAKE_TIMER, NULL, "Wake timer");
#endif
// We need this transition, because we might not transition if we were waiting to enter light-sleep, because when we wake from
// light sleep we _always_ transition to NB or dark and
@@ -279,7 +297,8 @@ void PowerFSM_setup()
powerFSM.add_transition(&stateLS, &stateON, EVENT_INPUT, NULL, "Input Device");
powerFSM.add_transition(&stateNB, &stateON, EVENT_INPUT, NULL, "Input Device");
powerFSM.add_transition(&stateDARK, &stateON, EVENT_INPUT, NULL, "Input Device");
powerFSM.add_transition(&stateON, &stateON, EVENT_INPUT, NULL, "Input Device"); // restarts the sleep timer
powerFSM.add_transition(&stateON, &stateON, EVENT_INPUT, NULL, "Input Device"); // restarts the sleep timer
powerFSM.add_transition(&statePOWER, &statePOWER, EVENT_INPUT, NULL, "Input Device"); // restarts the sleep timer
powerFSM.add_transition(&stateDARK, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing");
powerFSM.add_transition(&stateON, &stateON, EVENT_BLUETOOTH_PAIR, NULL, "Bluetooth pairing");

View File

@@ -21,7 +21,7 @@ class PowerFSMThread : public OSThread
/// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake
/// cpu for serial rx - FIXME)
auto state = powerFSM.getState();
const auto state = powerFSM.getState();
canSleep = (state != &statePOWER) && (state != &stateSERIAL);
if (powerStatus->getHasUSB()) {

View File

@@ -18,6 +18,12 @@ NoopPrint noopPrint;
#if HAS_WIFI || HAS_ETHERNET
extern Syslog syslog;
#endif
void RedirectablePrint::rpInit()
{
#ifdef HAS_FREE_RTOS
inDebugPrint = xSemaphoreCreateMutexStatic(&this->_MutexStorageSpace);
#endif
}
void RedirectablePrint::setDestination(Print *_dest)
{
@@ -66,9 +72,12 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
return 0;
}
size_t r = 0;
#ifdef HAS_FREE_RTOS
if (inDebugPrint != nullptr && xSemaphoreTake(inDebugPrint, portMAX_DELAY) == pdTRUE) {
#else
if (!inDebugPrint) {
inDebugPrint = true;
#endif
va_list arg;
va_start(arg, format);
@@ -141,7 +150,11 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
va_end(arg);
isContinuationMessage = !hasNewline;
#ifdef HAS_FREE_RTOS
xSemaphoreGive(inDebugPrint);
#else
inDebugPrint = false;
#endif
}
return r;

View File

@@ -1,5 +1,6 @@
#pragma once
#include "../freertosinc.h"
#include <Print.h>
#include <stdarg.h>
#include <string>
@@ -16,14 +17,19 @@ class RedirectablePrint : public Print
/// Used to allow multiple logDebug messages to appear on a single log line
bool isContinuationMessage = false;
#ifdef HAS_FREE_RTOS
SemaphoreHandle_t inDebugPrint = nullptr;
StaticSemaphore_t _MutexStorageSpace;
#else
volatile bool inDebugPrint = false;
#endif
public:
explicit RedirectablePrint(Print *_dest) : dest(_dest) {}
/**
* Set a new destination
*/
void rpInit();
void setDestination(Print *dest);
virtual size_t write(uint8_t c);
@@ -54,4 +60,4 @@ class NoopPrint : public Print
/**
* A printer that doesn't go anywhere
*/
extern NoopPrint noopPrint;
extern NoopPrint noopPrint;

View File

@@ -12,6 +12,7 @@ SerialConsole *console;
void consoleInit()
{
new SerialConsole(); // Must be dynamically allocated because we are now inheriting from thread
DEBUG_PORT.rpInit(); // Simply sets up semaphore
}
void consolePrintf(const char *format, ...)

View File

@@ -15,4 +15,6 @@ enum class Cmd {
PRINT,
START_SHUTDOWN_SCREEN,
START_REBOOT_SCREEN,
SHOW_PREV_FRAME,
SHOW_NEXT_FRAME
};

View File

@@ -98,8 +98,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Define if screen should be mirrored left to right
// #define SCREEN_MIRROR
// The m5stack I2C Keyboard (also RAK14004)
// I2C Keyboards (M5Stack, RAK14004, T-Deck)
#define CARDKB_ADDR 0x5F
#define TDECK_KB_ADDR 0x55
#define BBQ10_KB_ADDR 0x1F
// -----------------------------------------------------------------------------
// SENSOR
@@ -146,6 +148,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define GPS_THREAD_INTERVAL 100
#endif
// convert 24-bit color to 16-bit (56K)
#define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
/* Step #1: offer chance for variant-specific defines */
#include "variant.h"
@@ -173,6 +178,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifndef HAS_BUTTON
#define HAS_BUTTON 0
#endif
#ifndef HAS_TRACKBALL
#define HAS_TRACKBALL 0
#endif
#ifndef HAS_TOUCHSCREEN
#define HAS_TOUCHSCREEN 0
#endif
#ifndef HAS_TELEMETRY
#define HAS_TELEMETRY 0
#endif

View File

@@ -30,8 +30,8 @@ ScanI2C::FoundDevice ScanI2C::firstRTC() const
ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
{
ScanI2C::DeviceType types[] = {CARDKB, RAK14004};
return firstOfOrNONE(2, types);
ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004};
return firstOfOrNONE(4, types);
}
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const

View File

@@ -16,6 +16,8 @@ class ScanI2C
RTC_RV3028,
RTC_PCF8563,
CARDKB,
TDECKKB,
BBQ10KB,
RAK14004,
PMU_AXP192_AXP2101,
BME_680,

View File

@@ -212,6 +212,8 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
}
break;
SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard found\n");
SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10 keyboard found\n");
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "st7567 display found\n");
#ifdef HAS_NCP5623
SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623 RGB LED found\n");

View File

@@ -4,6 +4,10 @@
#include "configuration.h"
#include "sleep.h"
#ifdef ARCH_PORTDUINO
#include "meshUtils.h"
#endif
#ifndef GPS_RESET_MODE
#define GPS_RESET_MODE HIGH
#endif
@@ -43,13 +47,50 @@ void GPS::UBXChecksum(byte *message, size_t length)
message[length - 1] = CK_B;
}
bool GPS::getACK(uint8_t class_id, uint8_t msg_id)
GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
{
uint8_t buffer[768] = {0};
uint8_t b;
int bytesRead = 0;
uint32_t startTimeout = millis() + waitMillis;
while (millis() < startTimeout) {
if (_serial_gps->available()) {
b = _serial_gps->read();
buffer[bytesRead] = b;
bytesRead++;
if ((bytesRead == 767) || (b == '\r')) {
if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
#ifdef GPS_DEBUG
buffer[bytesRead] = '\0';
LOG_DEBUG("%s\r", (char *)buffer);
#endif
return GNSS_RESPONSE_OK;
} else {
#ifdef GPS_DEBUG
buffer[bytesRead] = '\0';
LOG_INFO("Bytes read:%s\n", (char *)buffer);
#endif
bytesRead = 0;
}
}
}
}
#ifdef GPS_DEBUG
buffer[bytesRead] = '\0';
LOG_INFO("Bytes read:%s\n", (char *)buffer);
#endif
return GNSS_RESPONSE_NONE;
}
GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis)
{
uint8_t b;
uint8_t ack = 0;
const uint8_t ackP[2] = {class_id, msg_id};
uint8_t buf[10] = {0xB5, 0x62, 0x05, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned long startTime = millis();
uint32_t startTime = millis();
const char frame_errors[] = "More than 100 frame errors";
int sCounter = 0;
for (int j = 2; j < 6; j++) {
buf[8] += buf[j];
@@ -62,28 +103,46 @@ bool GPS::getACK(uint8_t class_id, uint8_t msg_id)
buf[9] += buf[8];
}
while (1) {
while (millis() - startTime < waitMillis) {
if (ack > 9) {
// LOG_INFO("Got ACK for class %02X message %02X\n", class_id, msg_id);
return true; // ACK received
}
if (millis() - startTime > 3000) {
LOG_WARN("No response for class %02X message %02X\n", class_id, msg_id);
return false; // No response received within 3 seconds
#ifdef GPS_DEBUG
LOG_DEBUG("\n");
LOG_INFO("Got ACK for class %02X message %02X in %d millis.\n", class_id, msg_id, millis() - startTime);
#endif
return GNSS_RESPONSE_OK; // ACK received
}
if (_serial_gps->available()) {
b = _serial_gps->read();
if (b == frame_errors[sCounter]) {
sCounter++;
if (sCounter == 26) {
return GNSS_RESPONSE_FRAME_ERRORS;
}
} else {
sCounter = 0;
}
#ifdef GPS_DEBUG
LOG_DEBUG("%02X", b);
#endif
if (b == buf[ack]) {
ack++;
} else {
ack = 0; // Reset the acknowledgement counter
if (buf[3] == 0x00) { // UBX-ACK-NAK message
if (ack == 3 && b == 0x00) { // UBX-ACK-NAK message
#ifdef GPS_DEBUG
LOG_DEBUG("\n");
#endif
LOG_WARN("Got NAK for class %02X message %02X\n", class_id, msg_id);
return false; // NAK received
return GNSS_RESPONSE_NAK; // NAK received
}
ack = 0; // Reset the acknowledgement counter
}
}
}
#ifdef GPS_DEBUG
LOG_DEBUG("\n");
LOG_WARN("No response for class %02X message %02X\n", class_id, msg_id);
#endif
return GNSS_RESPONSE_NONE; // No response received within timeout
}
/**
@@ -95,14 +154,14 @@ bool GPS::getACK(uint8_t class_id, uint8_t msg_id)
* @param requestedID: request message ID constant
* @retval length of payload message
*/
int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID)
int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID, uint32_t waitMillis)
{
uint16_t ubxFrameCounter = 0;
uint32_t startTime = millis();
uint16_t needRead;
while (millis() - startTime < 1200) {
while (_serial_gps->available()) {
while (millis() - startTime < waitMillis) {
if (_serial_gps->available()) {
int c = _serial_gps->read();
switch (ubxFrameCounter) {
case 0:
@@ -144,8 +203,6 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
// Payload length msb
needRead |= (c << 8);
ubxFrameCounter++;
break;
case 6:
// Check for buffer overflow
if (needRead >= size) {
ubxFrameCounter = 0;
@@ -155,6 +212,10 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
ubxFrameCounter = 0;
} else {
// return payload length
#ifdef GPS_DEBUG
LOG_INFO("Got ACK for class %02X message %02X in %d millis.\n", requestedClass, requestedID,
millis() - startTime);
#endif
return needRead;
}
break;
@@ -164,6 +225,7 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
}
}
}
// LOG_WARN("No response for class %02X message %02X\n", requestedClass, requestedID);
return 0;
}
@@ -174,7 +236,7 @@ bool GPS::setupGPS()
#ifdef ARCH_ESP32
// In esp32 framework, setRxBufferSize needs to be initialized before Serial
_serial_gps->setRxBufferSize(2048); // the default is 256
_serial_gps->setRxBufferSize(SERIAL_BUFFER_SIZE); // the default is 256
#endif
// if the overrides are not dialled in, set them from the board definitions, if they exist
@@ -188,8 +250,8 @@ bool GPS::setupGPS()
config.position.tx_gpio = GPS_TX_PIN;
#endif
//#define BAUD_RATE 115200
// ESP32 has a special set of parameters vs other arduino ports
// #define BAUD_RATE 115200
// ESP32 has a special set of parameters vs other arduino ports
#if defined(ARCH_ESP32)
if (config.position.rx_gpio) {
LOG_DEBUG("Using GPIO%d for GPS RX\n", config.position.rx_gpio);
@@ -203,7 +265,21 @@ bool GPS::setupGPS()
/*
* T-Beam-S3-Core will be preset to use gps Probe here, and other boards will not be changed first
*/
gnssModel = probe();
#if defined(GPS_UC6580)
_serial_gps->updateBaudRate(115200);
gnssModel = GNSS_MODEL_UC6850;
#else
for (int serialSpeed : {9600, 4800, 38400, 57600, 115200}) {
LOG_DEBUG("Probing for GPS at %d \n", serialSpeed);
gnssModel = probe(serialSpeed);
if (gnssModel != GNSS_MODEL_UNKNOWN)
break;
}
if (gnssModel == GNSS_MODEL_UNKNOWN) {
LOG_DEBUG("No GPS found, retrying at 9600 baud.\n");
gnssModel = probe(9600);
}
#endif
if (gnssModel == GNSS_MODEL_MTK) {
/*
@@ -213,8 +289,8 @@ bool GPS::setupGPS()
// _serial_gps->begin(9600); //The baud rate of 9600 has been initialized at the beginning of setupGPS, this line
// is the redundant part delay(250);
// Initialize the L76K Chip, use GPS + GLONASS
_serial_gps->write("$PCAS04,5*1C\r\n");
// Initialize the L76K Chip, use GPS + GLONASS + BEIDOU
_serial_gps->write("$PCAS04,7*1E\r\n");
delay(250);
// only ask for RMC and GGA
_serial_gps->write("$PCAS03,1,0,0,0,1,0,0,0,0,0,,,0,0*02\r\n");
@@ -228,46 +304,88 @@ bool GPS::setupGPS()
_serial_gps->write("$CFGSYS,h15\r\n");
delay(250);
} else if (gnssModel == GNSS_MODEL_UBLOX) {
/*
uint8_t buffer[768] = {0};
byte _message_GNSS[8] = {0xb5, 0x62, // Sync message for UBX protocol
0x06, 0x3e, // Message class and ID (UBX-CFG-GNSS)
0x00, 0x00, // Length of payload (28 bytes)
0x00, 0x00};
UBXChecksum(_message_GNSS, sizeof(_message_GNSS));
// Send the message to the module
_serial_gps->write(_message_GNSS, sizeof(_message_GNSS));
int ackLen = getACK(buffer, sizeof(buffer), 0x06, 0x3e, 2000);
LOG_DEBUG("monver reply size = %d\n", ackLen);
LOG_DEBUG("Ack: ");
for (int i = 0; i < ackLen; i++) {
LOG_DEBUG("%02X", buffer[i]);
}
LOG_DEBUG("\n"); */
// Configure GNSS system to GPS+SBAS+GLONASS (Module may restart after this command)
// We need set it because by default it is GPS only, and we want to use GLONASS too
// Also we need SBAS for better accuracy and extra features
// ToDo: Dynamic configure GNSS systems depending of LoRa region
byte _message_GNSS[36] = {
0xb5, 0x62, // Sync message for UBX protocol
0x06, 0x3e, // Message class and ID (UBX-CFG-GNSS)
0x1c, 0x00, // Length of payload (28 bytes)
0x00, // msgVer (0 for this version)
0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
0x03, // numConfigBlocks (number of GNSS systems), most modules support maximum 3 GNSS systems
// GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, // GPS
0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01, // SBAS
0x06, 0x08, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x01, // GLONASS
0x00, 0x00 // Checksum (to be calculated below)
};
// Calculate the checksum and update the message.
UBXChecksum(_message_GNSS, sizeof(_message_GNSS));
if (strncmp(info.hwVersion, "00040007", 8) !=
0) { // The original ublox 6 is GPS only and doesn't support the UBX-CFG-GNSS message
if (strncmp(info.hwVersion, "00070000", 8) == 0) { // Max7 seems to only support GPS *or* GLONASS
LOG_DEBUG("Setting GPS+SBAS\n");
byte _message_GNSS[28] = {
0xb5, 0x62, // Sync message for UBX protocol
0x06, 0x3e, // Message class and ID (UBX-CFG-GNSS)
0x14, 0x00, // Length of payload (28 bytes)
0x00, // msgVer (0 for this version)
0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
0x02, // numConfigBlocks (number of GNSS systems), most modules support maximum 3 GNSS systems
// GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x00, 0x01, // GPS
0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x01, // SBAS
0x00, 0x00 // Checksum (to be calculated below)
};
// Calculate the checksum and update the message.
UBXChecksum(_message_GNSS, sizeof(_message_GNSS));
// Send the message to the module
_serial_gps->write(_message_GNSS, sizeof(_message_GNSS));
} else {
byte _message_GNSS[36] = {
0xb5, 0x62, // Sync message for UBX protocol
0x06, 0x3e, // Message class and ID (UBX-CFG-GNSS)
0x1c, 0x00, // Length of payload (28 bytes)
0x00, // msgVer (0 for this version)
0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
0x03, // numConfigBlocks (number of GNSS systems), most modules support maximum 3 GNSS systems
// GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, // GPS
0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01, // SBAS
0x06, 0x08, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x01, // GLONASS
0x00, 0x00 // Checksum (to be calculated below)
};
// Calculate the checksum and update the message.
UBXChecksum(_message_GNSS, sizeof(_message_GNSS));
// Send the message to the module
_serial_gps->write(_message_GNSS, sizeof(_message_GNSS));
}
// Send the message to the module
_serial_gps->write(_message_GNSS, sizeof(_message_GNSS));
if (!getACK(0x06, 0x3e)) {
// It's not critical if the module doesn't acknowledge this configuration.
// The module should operate adequately with its factory or previously saved settings.
// It appears that there is a firmware bug in some GPS modules: When an attempt is made
// to overwrite a saved state with identical values, no ACK/NAK is received, contrary to
// what is specified in the Ublox documentation.
// There is also a possibility that the module may be GPS-only.
LOG_INFO("Unable to reconfigure GNSS - defaults maintained. Is this module GPS-only?\n");
return true;
} else {
LOG_INFO("GNSS configured for GPS+SBAS+GLONASS. Pause for 0.75s before sending next command.\n");
// Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next commands
delay(750);
return true;
if (getACK(0x06, 0x3e, 800) == GNSS_RESPONSE_NAK) {
// It's not critical if the module doesn't acknowledge this configuration.
// The module should operate adequately with its factory or previously saved settings.
// It appears that there is a firmware bug in some GPS modules: When an attempt is made
// to overwrite a saved state with identical values, no ACK/NAK is received, contrary to
// what is specified in the Ublox documentation.
// There is also a possibility that the module may be GPS-only.
LOG_INFO("Unable to reconfigure GNSS - defaults maintained. Is this module GPS-only?\n");
} else {
if (strncmp(info.hwVersion, "00070000", 8) == 0) {
LOG_INFO("GNSS configured for GPS+SBAS. Pause for 0.75s before sending next command.\n");
} else {
LOG_INFO("GNSS configured for GPS+SBAS+GLONASS. Pause for 0.75s before sending next command.\n");
}
// Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next
// commands
delay(750);
}
}
// Enable interference resistance, because we are using LoRa, WiFi and Bluetooth on same board,
@@ -296,9 +414,8 @@ bool GPS::setupGPS()
// Send the message to the module
_serial_gps->write(_message_JAM, sizeof(_message_JAM));
if (!getACK(0x06, 0x39)) {
if (getACK(0x06, 0x39, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to enable interference resistance.\n");
return true;
}
// Configure navigation engine expert settings:
@@ -342,25 +459,10 @@ bool GPS::setupGPS()
// Send the message to the module
_serial_gps->write(_message_NAVX5, sizeof(_message_NAVX5));
if (!getACK(0x06, 0x23)) {
if (getACK(0x06, 0x23, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to configure extra settings.\n");
return true;
}
/*
tips: NMEA Only should not be set here, otherwise initializing Ublox gnss module again after
setting will not output command messages in UART1, resulting in unrecognized module information
// Set the UART port to output NMEA only
byte _message_nmea[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00,
0x80, 0x25, 0x00, 0x00, 0x07, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xAF};
_serial_gps->write(_message_nmea, sizeof(_message_nmea));
if (!getACK(0x06, 0x00)) {
LOG_WARN("Unable to enable NMEA Mode.\n");
return true;
}
*/
// ublox-M10S can be compatible with UBLOX traditional protocol, so the following sentence settings are also valid
// Set GPS update rate to 1Hz
@@ -383,9 +485,8 @@ bool GPS::setupGPS()
// Send the message to the module
_serial_gps->write(_message_1Hz, sizeof(_message_1Hz));
if (!getACK(0x06, 0x08)) {
if (getACK(0x06, 0x08, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to set GPS update rate.\n");
return true;
}
// Disable GGL. GGL - Geographic position (latitude and longitude), which provides the current geographical
@@ -407,9 +508,8 @@ bool GPS::setupGPS()
// Send the message to the module
_serial_gps->write(_message_GGL, sizeof(_message_GGL));
if (!getACK(0x06, 0x01)) {
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to disable NMEA GGL.\n");
return true;
}
// Enable GSA. GSA - GPS DOP and active satellites, used for detailing the satellites used in the positioning and
@@ -426,9 +526,8 @@ bool GPS::setupGPS()
};
UBXChecksum(_message_GSA, sizeof(_message_GSA));
_serial_gps->write(_message_GSA, sizeof(_message_GSA));
if (!getACK(0x06, 0x01)) {
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to Enable NMEA GSA.\n");
return true;
}
// Disable GSV. GSV - Satellites in view, details the number and location of satellites in view.
@@ -444,9 +543,8 @@ bool GPS::setupGPS()
};
UBXChecksum(_message_GSV, sizeof(_message_GSV));
_serial_gps->write(_message_GSV, sizeof(_message_GSV));
if (!getACK(0x06, 0x01)) {
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to disable NMEA GSV.\n");
return true;
}
// Disable VTG. VTG - Track made good and ground speed, which provides course and speed information relative to
@@ -463,9 +561,8 @@ bool GPS::setupGPS()
};
UBXChecksum(_message_VTG, sizeof(_message_VTG));
_serial_gps->write(_message_VTG, sizeof(_message_VTG));
if (!getACK(0x06, 0x01)) {
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to disable NMEA VTG.\n");
return true;
}
// Enable RMC. RMC - Recommended Minimum data, the essential gps pvt (position, velocity, time) data.
@@ -481,9 +578,8 @@ bool GPS::setupGPS()
};
UBXChecksum(_message_RMC, sizeof(_message_RMC));
_serial_gps->write(_message_RMC, sizeof(_message_RMC));
if (!getACK(0x06, 0x01)) {
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to enable NMEA RMC.\n");
return true;
}
// Enable GGA. GGA - Global Positioning System Fix Data, which provides 3D location and accuracy data.
@@ -499,9 +595,8 @@ bool GPS::setupGPS()
};
UBXChecksum(_message_GGA, sizeof(_message_GGA));
_serial_gps->write(_message_GGA, sizeof(_message_GGA));
if (!getACK(0x06, 0x01)) {
if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to enable NMEA GGA.\n");
return true;
}
// The Power Management configuration allows the GPS module to operate in different power modes for optimized power
@@ -517,27 +612,28 @@ bool GPS::setupGPS()
// set to Interval; otherwise, it must be set to '0'. The 'onTime' field specifies the duration of the ON phase and
// must be smaller than the period. It is only valid when the powerSetupValue is set to Interval; otherwise, it must
// be set to '0'.
byte UBX_CFG_PMS[14] = {
0xB5, 0x62, // UBX sync characters
0x06, 0x86, // Message class and ID (UBX-CFG-PMS)
0x06, 0x00, // Length of payload (6 bytes)
0x00, // Version (0)
0x03, // Power setup value
0x00, 0x00, // period: not applicable, set to 0
0x00, 0x00, // onTime: not applicable, set to 0
0x00, 0x00 // Placeholder for checksum, will be calculated next
};
if (uBloxProtocolVersion >= 18) {
byte UBX_CFG_PMS[16] = {
0xB5, 0x62, // UBX sync characters
0x06, 0x86, // Message class and ID (UBX-CFG-PMS)
0x08, 0x00, // Length of payload (6 bytes)
0x00, // Version (0)
0x03, // Power setup value
0x00, 0x00, // period: not applicable, set to 0
0x00, 0x00, // onTime: not applicable, set to 0
0x97, 0x6F, // reserved, generated by u-center
0x00, 0x00 // Placeholder for checksum, will be calculated next
};
// Calculate the checksum and update the message
UBXChecksum(UBX_CFG_PMS, sizeof(UBX_CFG_PMS));
// Calculate the checksum and update the message
UBXChecksum(UBX_CFG_PMS, sizeof(UBX_CFG_PMS));
// Send the message to the module
_serial_gps->write(UBX_CFG_PMS, sizeof(UBX_CFG_PMS));
if (!getACK(0x06, 0x86)) {
LOG_WARN("Unable to enable powersaving for GPS.\n");
return true;
// Send the message to the module
_serial_gps->write(UBX_CFG_PMS, sizeof(UBX_CFG_PMS));
if (getACK(0x06, 0x86, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to enable powersaving for GPS.\n");
}
}
// We need save configuration to flash to make our config changes persistent
byte _message_SAVE[21] = {
0xB5, 0x62, // UBX protocol header
@@ -556,12 +652,10 @@ bool GPS::setupGPS()
// Send the message to the module
_serial_gps->write(_message_SAVE, sizeof(_message_SAVE));
if (!getACK(0x06, 0x09)) {
if (getACK(0x06, 0x09, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to save GNSS module configuration.\n");
return true;
} else {
LOG_INFO("GNSS module configuration saved!\n");
return true;
}
}
}
@@ -675,6 +769,7 @@ void GPS::setAwake(bool on)
if (isAwake != on) {
LOG_DEBUG("WANT GPS=%d\n", on);
if (on) {
clearBuffer(); // drop any old data waiting in the buffer
lastWakeStartMsec = millis();
wake();
} else {
@@ -805,15 +900,6 @@ int32_t GPS::runOnce()
// If state has changed do a publish
publishUpdate();
if (!(fixeddelayCtr >= 20) && config.position.fixed_position && hasValidLocation) {
fixeddelayCtr++;
// LOG_DEBUG("Our delay counter is %d\n", fixeddelayCtr);
if (fixeddelayCtr >= 20) {
doGPSpowersave(false);
forceWake(false);
}
}
// 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms
// if not awake we can run super infrquently (once every 5 secs?) to see if we need to wake.
return isAwake ? GPS_THREAD_INTERVAL : 5000;
@@ -834,6 +920,14 @@ void GPS::forceWake(bool on)
}
}
// clear the GPS rx buffer as quickly as possible
void GPS::clearBuffer()
{
int x = _serial_gps->available();
while (x--)
_serial_gps->read();
}
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
int GPS::prepareSleep(void *unused)
{
@@ -855,50 +949,72 @@ int GPS::prepareDeepSleep(void *unused)
return 0;
}
GnssModel_t GPS::probe()
GnssModel_t GPS::probe(int serialSpeed)
{
memset(&info, 0, sizeof(struct uBloxGnssModelInfo));
// return immediately if the model is set by the variant.h file
//#ifdef GPS_UBLOX (unless it's a ublox, because we might want to know the module info!
// return GNSS_MODEL_UBLOX; think about removing this macro and return)
#if defined(GPS_L76K)
return GNSS_MODEL_MTK;
#elif defined(GPS_UC6580)
_serial_gps->updateBaudRate(115200);
return GNSS_MODEL_UC6850;
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040)
_serial_gps->end();
_serial_gps->begin(serialSpeed);
#else
uint8_t buffer[384] = {0};
if (_serial_gps->baudRate() != serialSpeed) {
LOG_DEBUG("Setting Baud to %i\n", serialSpeed);
_serial_gps->updateBaudRate(serialSpeed);
}
#endif
#ifdef GPS_DEBUG
for (int i = 0; i < 20; i++) {
getACK("$GP", 200);
}
#endif
memset(&info, 0, sizeof(struct uBloxGnssModelInfo));
uint8_t buffer[768] = {0};
delay(100);
// Close all NMEA sentences , Only valid for MTK platform
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20);
// Get version information
clearBuffer();
_serial_gps->write("$PCAS06,0*1B\r\n");
uint32_t startTimeout = millis() + 500;
while (millis() < startTimeout) {
if (_serial_gps->available()) {
String ver = _serial_gps->readStringUntil('\r');
// Get module info , If the correct header is returned,
// it can be determined that it is the MTK chip
int index = ver.indexOf("$");
if (index != -1) {
ver = ver.substring(index);
if (ver.startsWith("$GPTXT,01,01,02")) {
LOG_INFO("L76K GNSS init succeeded, using L76K GNSS Module\n");
return GNSS_MODEL_MTK;
}
}
}
if (getACK("$GPTXT,01,01,02,SW=", 500) == GNSS_RESPONSE_OK) {
LOG_INFO("L76K GNSS init succeeded, using L76K GNSS Module\n");
return GNSS_MODEL_MTK;
}
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x0E, 0x30};
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
UBXChecksum(cfg_rate, sizeof(cfg_rate));
clearBuffer();
_serial_gps->write(cfg_rate, sizeof(cfg_rate));
// Check that the returned response class and message ID are correct
if (!getAck(buffer, 384, 0x06, 0x08)) {
LOG_WARN("Failed to find UBlox & MTK GNSS Module\n");
GPS_RESPONSE response = getACK(0x06, 0x08, 750);
if (response == GNSS_RESPONSE_NONE) {
LOG_WARN("Failed to find UBlox & MTK GNSS Module using baudrate %d\n", serialSpeed);
return GNSS_MODEL_UNKNOWN;
} else if (response == GNSS_RESPONSE_FRAME_ERRORS) {
LOG_INFO("UBlox Frame Errors using baudrate %d\n", serialSpeed);
} else if (response == GNSS_RESPONSE_OK) {
LOG_INFO("Found a UBlox Module using baudrate %d\n", serialSpeed);
}
// tips: NMEA Only should not be set here, otherwise initializing Ublox gnss module again after
// setting will not output command messages in UART1, resulting in unrecognized module information
if (serialSpeed != 9600) {
// Set the UART port to 9600
byte _message_prt[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00,
0x80, 0x25, 0x00, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
UBXChecksum(_message_prt, sizeof(_message_prt));
_serial_gps->write(_message_prt, sizeof(_message_prt));
delay(500);
serialSpeed = 9600;
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040)
_serial_gps->end();
_serial_gps->begin(serialSpeed);
#else
_serial_gps->updateBaudRate(serialSpeed);
#endif
delay(200);
}
memset(buffer, 0, sizeof(buffer));
byte _message_MONVER[8] = {
0xB5, 0x62, // Sync message for UBX protocol
@@ -908,9 +1024,10 @@ GnssModel_t GPS::probe()
};
// Get Ublox gnss module hardware and software info
UBXChecksum(_message_MONVER, sizeof(_message_MONVER));
clearBuffer();
_serial_gps->write(_message_MONVER, sizeof(_message_MONVER));
uint16_t len = getAck(buffer, 384, 0x0A, 0x04);
uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200);
if (len) {
// LOG_DEBUG("monver reply size = %d\n", len);
uint16_t position = 0;
@@ -919,13 +1036,13 @@ GnssModel_t GPS::probe()
position++;
}
for (int i = 0; i < 10; i++) {
info.hwVersion[i] = buffer[position - 1];
info.hwVersion[i] = buffer[position];
position++;
}
while (len >= position + 30) {
for (int i = 0; i < 30; i++) {
info.extension[info.extensionNo][i] = buffer[position - 1];
info.extension[info.extensionNo][i] = buffer[position];
position++;
}
info.extensionNo++;
@@ -935,7 +1052,6 @@ GnssModel_t GPS::probe()
LOG_DEBUG("Module Info : \n");
LOG_DEBUG("Soft version: %s\n", info.swVersion);
LOG_DEBUG("first char is %c\n", (char)info.swVersion[0]);
LOG_DEBUG("Hard version: %s\n", info.hwVersion);
LOG_DEBUG("Extensions:%d\n", info.extensionNo);
for (int i = 0; i < info.extensionNo; i++) {
@@ -954,7 +1070,7 @@ GnssModel_t GPS::probe()
} else {
LOG_INFO("UBlox GNSS init succeeded, using UBlox GNSS Module\n");
}
} else if (!strncmp(info.extension[i], "PROTVER=", 8)) {
} else if (!strncmp(info.extension[i], "PROTVER", 7)) {
char *ptr = nullptr;
memset(buffer, 0, sizeof(buffer));
strncpy((char *)buffer, &(info.extension[i][8]), sizeof(buffer));
@@ -970,7 +1086,6 @@ GnssModel_t GPS::probe()
}
return GNSS_MODEL_UBLOX;
#endif
}
#if HAS_GPS

View File

@@ -18,6 +18,13 @@ typedef enum {
GNSS_MODEL_UNKNOWN,
} GnssModel_t;
typedef enum {
GNSS_RESPONSE_NONE,
GNSS_RESPONSE_NAK,
GNSS_RESPONSE_FRAME_ERRORS,
GNSS_RESPONSE_OK,
} GPS_RESPONSE;
// Generate a string representation of DOP
const char *getDOPString(uint32_t dop);
@@ -91,6 +98,9 @@ class GPS : private concurrency::OSThread
// Some GPS modules (ublock) require factory reset
virtual bool factoryReset() { return true; }
// Empty the input buffer as quickly as possible
void clearBuffer();
protected:
/// Do gps chipset specific init, return true for success
virtual bool setupGPS();
@@ -158,8 +168,6 @@ class GPS : private concurrency::OSThread
*/
uint32_t getSleepTime() const;
bool getACK(uint8_t c, uint8_t i);
/**
* Tell users we have new GPS readings
*/
@@ -169,10 +177,11 @@ class GPS : private concurrency::OSThread
// Get GNSS model
String getNMEA();
GnssModel_t probe();
int getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID);
GnssModel_t probe(int serialSpeed);
int getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID, uint32_t waitMillis);
GPS_RESPONSE getACK(uint8_t c, uint8_t i, uint32_t waitMillis);
GPS_RESPONSE getACK(const char *message, uint32_t waitMillis);
// delay counter to allow more sats before fixed position stops GPS thread
uint8_t fixeddelayCtr = 0;

View File

@@ -107,6 +107,14 @@ bool NMEAGPS::lookForLocation()
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
fixQual = reader.fixQuality();
#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,
reader.failedChecksum());
lastChecksumFailCount = reader.failedChecksum();
}
#endif
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
fixType = atoi(gsafixtype.value()); // will set to zero if no data
// LOG_DEBUG("FIX QUAL=%d, TYPE=%d\n", fixQual, fixType);
@@ -174,8 +182,10 @@ bool NMEAGPS::lookForLocation()
#endif
// Discard incomplete or erroneous readings
if (reader.hdop.value() == 0)
if (reader.hdop.value() == 0) {
LOG_WARN("BOGUS hdop.value() REJECTED: %d\n", reader.hdop.value());
return false;
}
p.latitude_i = toDegInt(loc.lat);
p.longitude_i = toDegInt(loc.lng);
@@ -243,7 +253,14 @@ bool NMEAGPS::hasFlow()
bool NMEAGPS::whileIdle()
{
bool isValid = false;
#ifdef SERIAL_BUFFER_SIZE
if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) {
LOG_WARN("GPS Buffer full with %u bytes waiting. Flushing to avoid corruption.\n", _serial_gps->available());
clearBuffer();
}
#endif
// if (_serial_gps->available() > 0)
// LOG_DEBUG("GPS Bytes Waiting: %u\n", _serial_gps->available());
// First consume any chars that have piled up at the receiver
while (_serial_gps->available() > 0) {
int c = _serial_gps->read();
@@ -252,4 +269,4 @@ bool NMEAGPS::whileIdle()
}
return isValid;
}
}

View File

@@ -13,6 +13,7 @@ class NMEAGPS : public GPS
{
TinyGPSPlus reader;
uint8_t fixQual = 0; // fix quality from GPGGA
uint32_t lastChecksumFailCount = 0;
#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
// (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
@@ -53,4 +54,4 @@ class NMEAGPS : public GPS
virtual bool hasLock() override;
virtual bool hasFlow() override;
};
};

View File

@@ -17,6 +17,10 @@ static uint32_t
timeStartMsec; // Once we have a GPS lock, this is where we hold the initial msec clock that corresponds to that time
static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only updated once on initial lock
/**
* Reads the current date and time from the RTC module and updates the system time.
* @return True if the RTC was successfully read and the system time was updated, false otherwise.
*/
void readFromRTC()
{
struct timeval tv; /* btw settimeofday() is helpful here too*/
@@ -83,7 +87,15 @@ void readFromRTC()
#endif
}
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
/**
* Sets the RTC (Real-Time Clock) if the provided time is of higher quality than the current RTC time.
*
* @param q The quality of the provided time.
* @param tv A pointer to a timeval struct containing the time to potentially set the RTC to.
* @return True if the RTC was set, false otherwise.
*
* If we haven't yet set our RTC this boot, set it from a GPS derived time
*/
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
{
static uint32_t lastSetMsec = 0;
@@ -151,6 +163,13 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
}
}
/**
* Sets the RTC time if the provided time is of higher quality than the current RTC time.
*
* @param q The quality of the provided time.
* @param t The time to potentially set the RTC to.
* @return True if the RTC was set to the provided time, false otherwise.
*/
bool perhapsSetRTC(RTCQuality q, struct tm &t)
{
/* Convert to unix time
@@ -171,11 +190,22 @@ bool perhapsSetRTC(RTCQuality q, struct tm &t)
}
}
/**
* Returns the current time in seconds since the Unix epoch (January 1, 1970).
*
* @return The current time in seconds since the Unix epoch.
*/
uint32_t getTime()
{
return (((uint32_t)millis() - timeStartMsec) / 1000) + zeroOffsetSecs;
}
/**
* Returns the current time from the RTC if the quality of the time is at least minQuality.
*
* @param minQuality The minimum quality of the RTC time required for it to be considered valid.
* @return The current time from the RTC if it meets the minimum quality requirement, or 0 if the time is not valid.
*/
uint32_t getValidTime(RTCQuality minQuality)
{
return (currentQuality >= minQuality) ? getTime() : 0;

View File

@@ -3,7 +3,7 @@
#include <OLEDDisplay.h>
/**
* An adapter class that allows using the TFT_eSPI library as if it was an OLEDDisplay implementation.
* An adapter class that allows using the GxEPD2 library as if it was an OLEDDisplay implementation.
*
* Remaining TODO:
* optimize display() to only draw changed pixels (see other OLED subclasses for examples)

View File

@@ -31,15 +31,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "gps/GeoCoord.h"
#include "gps/RTC.h"
#include "graphics/images.h"
#include "input/TouchScreenImpl1.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "mesh/Channels.h"
#include "mesh/generated/meshtastic/deviceonly.pb.h"
#include "meshUtils.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
#include "sleep.h"
#include "target_specific.h"
#include "utils.h"
#ifdef ARCH_ESP32
#include "esp_task_wdt.h"
@@ -102,7 +103,8 @@ 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)
#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
@@ -363,7 +365,7 @@ static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *sta
// Ignore messages originating from phone (from the current node 0x0) unless range test or store and forward module are enabled
static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
{
return packet->from != 0 && !moduleConfig.range_test.enabled && !moduleConfig.store_forward.enabled;
return packet->from != 0 && !moduleConfig.store_forward.enabled;
}
/// Draw the last text message we received
@@ -372,7 +374,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
// the max length of this buffer is much longer than we can possibly print
static char tempBuf[237];
meshtastic_MeshPacket &mp = devicestate.rx_text_message;
const meshtastic_MeshPacket &mp = devicestate.rx_text_message;
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);
@@ -488,11 +490,12 @@ static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *img
}
// Draw nodes status
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, NodeStatus *nodeStatus)
static void drawNodes(OLEDDisplay *display, int16_t x, int16_t y, const NodeStatus *nodeStatus)
{
char usersString[20];
snprintf(usersString, sizeof(usersString), "%d/%d", nodeStatus->getNumOnline(), nodeStatus->getNumTotal());
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x, y + 3, 8, 8, imgUser);
#else
display->drawFastImage(x, y, 8, 8, imgUser);
@@ -835,7 +838,11 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
}
static char distStr[20];
strncpy(distStr, "? km", sizeof(distStr)); // might not have location data
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
strncpy(distStr, "? mi", sizeof(distStr)); // might not have location data
} else {
strncpy(distStr, "? km", sizeof(distStr));
}
meshtastic_NodeInfoLite *ourNode = nodeDB.getMeshNode(nodeDB.getNodeNum());
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
int16_t compassX = 0, compassY = 0;
@@ -851,14 +858,14 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
bool hasNodeHeading = false;
if (ourNode && hasValidPosition(ourNode)) {
meshtastic_PositionLite &op = ourNode->position;
const meshtastic_PositionLite &op = ourNode->position;
float myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
drawCompassNorth(display, compassX, compassY, myHeading);
if (hasValidPosition(node)) {
// display direction toward node
hasNodeHeading = true;
meshtastic_PositionLite &p = node->position;
const meshtastic_PositionLite &p = node->position;
float d =
GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
@@ -1040,12 +1047,19 @@ void Screen::setup()
#endif
serialSinceMsec = millis();
#if HAS_TOUCHSCREEN
touchScreenImpl1 = new TouchScreenImpl1(dispdev.getWidth(), dispdev.getHeight(), dispdev.getTouch);
touchScreenImpl1->init();
#endif
// Subscribe to status updates
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
if (textMessageModule)
textMessageObserver.observe(textMessageModule);
if (inputBroker)
inputObserver.observe(inputBroker);
// Modules can notify screen about refresh
MeshModule::observeUIEvents(&uiFrameEventObserver);
@@ -1123,6 +1137,12 @@ int32_t Screen::runOnce()
handleOnPress();
}
break;
case Cmd::SHOW_PREV_FRAME:
handleShowPrevFrame();
break;
case Cmd::SHOW_NEXT_FRAME:
handleShowNextFrame();
break;
case Cmd::START_BLUETOOTH_PIN_SCREEN:
handleStartBluetoothPinScreen(cmd.bluetooth_pin);
break;
@@ -1416,6 +1436,28 @@ void Screen::handleOnPress()
}
}
void Screen::handleShowPrevFrame()
{
// If screen was off, just wake it, otherwise go back to previous frame
// If we are in a transition, the press must have bounced, drop it.
if (ui.getUiState()->frameState == FIXED) {
ui.previousFrame();
lastScreenTransition = millis();
setFastFramerate();
}
}
void Screen::handleShowNextFrame()
{
// If screen was off, just wake it, otherwise advance to next frame
// If we are in a transition, the press must have bounced, drop it.
if (ui.getUiState()->frameState == FIXED) {
ui.nextFrame();
lastScreenTransition = millis();
setFastFramerate();
}
}
#ifndef SCREEN_TRANSITION_FRAMERATE
#define SCREEN_TRANSITION_FRAMERATE 30 // fps
#endif
@@ -1488,7 +1530,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
#ifdef ARCH_ESP32
if (millis() - storeForwardModule->lastHeartbeat >
(storeForwardModule->heartbeatInterval * 1200)) { // no heartbeat, overlap a bit
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
imgQuestionL1);
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8,
@@ -1498,7 +1541,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
imgQuestion);
#endif
} else {
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 16, 8,
imgSFL1);
display->drawFastImage(x + SCREEN_WIDTH - 18 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 16, 8,
@@ -1510,7 +1554,8 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
}
#endif
} else {
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 3 + FONT_HEIGHT_SMALL, 12, 8,
imgInfoL1);
display->drawFastImage(x + SCREEN_WIDTH - 14 - display->getStringWidth(ourId), y + 11 + FONT_HEIGHT_SMALL, 12, 8,
@@ -1853,6 +1898,20 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
return 0;
}
int Screen::handleInputEvent(const InputEvent *event)
{
if (showingNormalScreen && moduleFrames.size() == 0) {
// LOG_DEBUG("Screen::handleInputEvent from %s\n", event->source);
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
showPrevFrame();
} else if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
showNextFrame();
}
}
return 0;
}
} // namespace graphics
#else
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}

View File

@@ -53,6 +53,7 @@ class Screen
#include "commands.h"
#include "concurrency/LockGuard.h"
#include "concurrency/OSThread.h"
#include "input/InputBroker.h"
#include "mesh/MeshModule.h"
#include "power.h"
#include <string>
@@ -118,6 +119,8 @@ class Screen : public concurrency::OSThread
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage);
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent);
CallbackObserver<Screen, const InputEvent *> inputObserver =
CallbackObserver<Screen, const InputEvent *>(this, &Screen::handleInputEvent);
public:
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
@@ -152,8 +155,10 @@ class Screen : public concurrency::OSThread
void blink();
/// Handles a button press.
/// Handle button press, trackball or swipe action)
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); }
void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); }
// Implementation to Adjust Brightness
void adjustBrightness();
@@ -301,9 +306,11 @@ class Screen : public concurrency::OSThread
// Use this handle to set things like battery status, user count, GPS status, etc.
DebugInfo *debug_info() { return &debugInfo; }
// Handle observer events
int handleStatusUpdate(const meshtastic::Status *arg);
int handleTextMessage(const meshtastic_MeshPacket *arg);
int handleUIFrameEvent(const UIFrameEvent *arg);
int handleInputEvent(const InputEvent *arg);
/// Used to force (super slow) eink displays to draw critical frames
void forceDisplay();
@@ -313,13 +320,6 @@ class Screen : public concurrency::OSThread
void setWelcomeFrames();
void getTouch(int *x, int *y)
{
#if defined(ST7735_CS) || defined(ILI9341_DRIVER) || defined(ST7789_CS)
dispdev.getTouch(x, y);
#endif
};
protected:
/// Updates the UI.
//
@@ -350,6 +350,8 @@ class Screen : public concurrency::OSThread
// Implementations of various commands, called from doTask().
void handleSetOn(bool on);
void handleOnPress();
void handleShowNextFrame();
void handleShowPrevFrame();
void handleStartBluetoothPinScreen(uint32_t pin);
void handlePrint(const char *text);
void handleStartFirmwareUpdateScreen();

View File

@@ -4,9 +4,9 @@
#define TFT_BACKLIGHT_ON HIGH
#endif
// convert 24-bit color to 16-bit (56K)
#define COLOR565(r, g, b) (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3))
#ifndef TFT_MESH
#define TFT_MESH COLOR565(0x67, 0xEA, 0x94)
#endif
#if defined(ST7735S)
#include <LovyanGFX.hpp> // Graphics and font library for ST7735 driver chip
@@ -108,10 +108,12 @@ class LGFX : public lgfx::LGFX_Device
lgfx::Panel_ST7789 _panel_instance;
lgfx::Bus_SPI _bus_instance;
lgfx::Light_PWM _light_instance;
#if HAS_TOUCHSCREEN
#ifdef T_WATCH_S3
lgfx::Touch_FT5x06 _touch_instance;
#else
lgfx::Touch_GT911 _touch_instance;
#endif
#endif
public:
@@ -126,13 +128,14 @@ class LGFX : public lgfx::LGFX_Device
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing
// 80MHz by an integer)
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin
cfg.use_lock = true; // Set to true to use transaction locking
cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch /
cfg.pin_sclk = ST7789_SCK; // Set SPI SCLK pin number
cfg.pin_mosi = ST7789_SDA; // Set SPI MOSI pin number
cfg.pin_miso = ST7789_MISO; // Set SPI MISO pin number (-1 = disable)
cfg.pin_dc = ST7789_RS; // Set SPI DC pin number (-1 = disable)
cfg.spi_3wire = false;
cfg.use_lock = true; // Set to true to use transaction locking
cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch /
// SPI_DMA_CH_AUTO=auto setting)
cfg.pin_sclk = ST7789_SCK; // Set SPI SCLK pin number
cfg.pin_mosi = ST7789_SDA; // Set SPI MOSI pin number
cfg.pin_miso = ST7789_MISO; // Set SPI MISO pin number (-1 = disable)
cfg.pin_dc = ST7789_RS; // Set SPI DC pin number (-1 = disable)
_bus_instance.config(cfg); // applies the set value to the bus.
_panel_instance.setBus(&_bus_instance); // set the bus on the panel.
@@ -148,16 +151,16 @@ class LGFX : public lgfx::LGFX_Device
// The following setting values are general initial values for each panel, so please comment out any
// unknown items and try them.
cfg.panel_width = TFT_WIDTH; // actual displayable width
cfg.panel_height = TFT_HEIGHT; // actual displayable height
cfg.offset_x = TFT_OFFSET_X; // Panel offset amount in X direction
cfg.offset_y = TFT_OFFSET_Y; // Panel offset amount in Y direction
cfg.offset_rotation = 0; // Rotation direction value offset 0~7 (4~7 is mirrored)
cfg.dummy_read_pixel = 9; // Number of bits for dummy read before pixel readout
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
cfg.readable = true; // Set to true if data can be read
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
cfg.panel_width = TFT_WIDTH; // actual displayable width
cfg.panel_height = TFT_HEIGHT; // actual displayable height
cfg.offset_x = TFT_OFFSET_X; // Panel offset amount in X direction
cfg.offset_y = TFT_OFFSET_Y; // Panel offset amount in Y direction
cfg.offset_rotation = TFT_OFFSET_ROTATION; // Rotation direction value offset 0~7 (4~7 is mirrored)
cfg.dummy_read_pixel = 9; // Number of bits for dummy read before pixel readout
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
cfg.readable = true; // Set to true if data can be read
cfg.invert = true; // Set to true if the light/darkness of the panel is reversed
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
cfg.dlen_16bit =
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
@@ -174,13 +177,14 @@ class LGFX : public lgfx::LGFX_Device
auto cfg = _light_instance.config(); // Gets a structure for backlight settings.
cfg.pin_bl = ST7789_BL; // Pin number to which the backlight is connected
cfg.invert = true; // true to invert the brightness of the backlight
cfg.invert = false; // true to invert the brightness of the backlight
// cfg.pwm_channel = 0;
_light_instance.config(cfg);
_panel_instance.setLight(&_light_instance); // Set the backlight on the panel.
}
#if HAS_TOUCHSCREEN
// Configure settings for touch screen control.
{
auto cfg = _touch_instance.config();
@@ -192,11 +196,11 @@ class LGFX : public lgfx::LGFX_Device
cfg.y_max = TFT_WIDTH - 1;
cfg.pin_int = SCREEN_TOUCH_INT;
cfg.bus_shared = true;
cfg.offset_rotation = 0;
cfg.offset_rotation = TFT_OFFSET_ROTATION;
// cfg.freq = 2500000;
// I2C
cfg.i2c_port = 1;
cfg.i2c_port = TOUCH_I2C_PORT;
cfg.i2c_addr = TOUCH_SLAVE_ADDRESS;
#ifdef SCREEN_TOUCH_USE_I2C1
cfg.pin_sda = I2C_SDA1;
@@ -205,11 +209,12 @@ class LGFX : public lgfx::LGFX_Device
cfg.pin_sda = I2C_SDA;
cfg.pin_scl = I2C_SCL;
#endif
cfg.freq = 400000;
// cfg.freq = 400000;
_touch_instance.config(cfg);
_panel_instance.setTouch(&_touch_instance);
}
#endif
setPanel(&_panel_instance); // Sets the panel to use.
}
@@ -217,7 +222,96 @@ class LGFX : public lgfx::LGFX_Device
static LGFX tft;
#elif defined(ST7735_CS) || defined(ILI9341_DRIVER)
#elif defined(ILI9341_DRIVER)
#include <LovyanGFX.hpp> // Graphics and font library for ILI9341 driver chip
#if defined(ILI9341_BACKLIGHT_EN) && !defined(TFT_BL)
#define TFT_BL ILI9341_BACKLIGHT_EN
#endif
class LGFX : public lgfx::LGFX_Device
{
lgfx::Panel_ILI9341 _panel_instance;
lgfx::Bus_SPI _bus_instance;
lgfx::Light_PWM _light_instance;
public:
LGFX(void)
{
{
auto cfg = _bus_instance.config();
// configure SPI
cfg.spi_host = ILI9341_SPI_HOST; // ESP32-S2,S3,C3 : SPI2_HOST or SPI3_HOST / ESP32 : VSPI_HOST or HSPI_HOST
cfg.spi_mode = 0;
cfg.freq_write = SPI_FREQUENCY; // SPI clock for transmission (up to 80MHz, rounded to the value obtained by dividing
// 80MHz by an integer)
cfg.freq_read = SPI_READ_FREQUENCY; // SPI clock when receiving
cfg.spi_3wire = false; // Set to true if reception is done on the MOSI pin
cfg.use_lock = true; // Set to true to use transaction locking
cfg.dma_channel = SPI_DMA_CH_AUTO; // SPI_DMA_CH_AUTO; // Set DMA channel to use (0=not use DMA / 1=1ch / 2=ch /
// SPI_DMA_CH_AUTO=auto setting)
cfg.pin_sclk = TFT_SCLK; // Set SPI SCLK pin number
cfg.pin_mosi = TFT_MOSI; // Set SPI MOSI pin number
cfg.pin_miso = TFT_MISO; // Set SPI MISO pin number (-1 = disable)
cfg.pin_dc = TFT_DC; // Set SPI DC pin number (-1 = disable)
_bus_instance.config(cfg); // applies the set value to the bus.
_panel_instance.setBus(&_bus_instance); // set the bus on the panel.
}
{ // Set the display panel control.
auto cfg = _panel_instance.config(); // Gets a structure for display panel settings.
cfg.pin_cs = TFT_CS; // Pin number where CS is connected (-1 = disable)
cfg.pin_rst = TFT_RST; // Pin number where RST is connected (-1 = disable)
cfg.pin_busy = TFT_BUSY; // Pin number where BUSY is connected (-1 = disable)
// The following setting values are general initial values for each panel, so please comment out any
// unknown items and try them.
cfg.panel_width = TFT_WIDTH; // actual displayable width
cfg.panel_height = TFT_HEIGHT; // actual displayable height
cfg.offset_x = TFT_OFFSET_X; // Panel offset amount in X direction
cfg.offset_y = TFT_OFFSET_Y; // Panel offset amount in Y direction
cfg.offset_rotation = 0; // Rotation direction value offset 0~7 (4~7 is upside down)
cfg.dummy_read_pixel = 8; // Number of bits for dummy read before pixel readout
cfg.dummy_read_bits = 1; // Number of bits for dummy read before non-pixel data read
cfg.readable = true; // Set to true if data can be read
cfg.invert = false; // Set to true if the light/darkness of the panel is reversed
cfg.rgb_order = false; // Set to true if the panel's red and blue are swapped
cfg.dlen_16bit =
false; // Set to true for panels that transmit data length in 16-bit units with 16-bit parallel or SPI
cfg.bus_shared = true; // If the bus is shared with the SD card, set to true (bus control with drawJpgFile etc.)
// Set the following only when the display is shifted with a driver with a variable number of pixels, such as the
// ST7735 or ILI9163.
cfg.memory_width = TFT_WIDTH; // Maximum width supported by the driver IC
cfg.memory_height = TFT_HEIGHT; // Maximum height supported by the driver IC
_panel_instance.config(cfg);
}
// Set the backlight control
{
auto cfg = _light_instance.config(); // Gets a structure for backlight settings.
cfg.pin_bl = TFT_BL; // Pin number to which the backlight is connected
cfg.invert = false; // true to invert the brightness of the backlight
// cfg.freq = 44100; // PWM frequency of backlight
// cfg.pwm_channel = 1; // PWM channel number to use
_light_instance.config(cfg);
_panel_instance.setLight(&_light_instance); // Set the backlight on the panel.
}
setPanel(&_panel_instance);
}
};
static LGFX tft;
#elif defined(ST7735_CS)
#include <TFT_eSPI.h> // Graphics and font library for ILI9341 driver chip
static TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
@@ -275,6 +369,9 @@ void TFTDisplay::sendCommand(uint8_t com)
#endif
#ifdef VTFT_CTRL
digitalWrite(VTFT_CTRL, LOW);
#endif
#ifndef M5STACK
tft.setBrightness(128);
#endif
break;
}
@@ -284,6 +381,9 @@ void TFTDisplay::sendCommand(uint8_t com)
#endif
#ifdef VTFT_CTRL
digitalWrite(VTFT_CTRL, HIGH);
#endif
#ifndef M5STACK
tft.setBrightness(0);
#endif
break;
}
@@ -294,6 +394,32 @@ void TFTDisplay::sendCommand(uint8_t com)
// Drop all other commands to device (we just update the buffer)
}
void TFTDisplay::flipScreenVertically()
{
#if defined(T_WATCH_S3)
LOG_DEBUG("Flip TFT vertically\n"); // T-Watch S3 right-handed orientation
tft.setRotation(0);
#endif
}
bool TFTDisplay::hasTouch(void)
{
#ifndef M5STACK
return tft.touch() != nullptr;
#else
return false;
#endif
}
bool TFTDisplay::getTouch(int16_t *x, int16_t *y)
{
#ifndef M5STACK
return tft.getTouch(x, y);
#else
return false;
#endif
}
void TFTDisplay::setDetected(uint8_t detected)
{
(void)detected;
@@ -311,10 +437,12 @@ bool TFTDisplay::connect()
#endif
tft.init();
#if defined(M5STACK) || defined(T_DECK)
tft.setRotation(1); // M5Stack/T-Deck have the TFT in landscape
#if defined(M5STACK)
tft.setRotation(0);
#elif defined(T_DECK) || defined(PICOMPUTER_S3)
tft.setRotation(1); // T-Deck has the TFT in landscape
#elif defined(T_WATCH_S3)
tft.setRotation(0); // T-Watch S3 has the TFT in portrait
tft.setRotation(2); // T-Watch S3 left-handed orientation
#else
tft.setRotation(3); // Orient horizontal and wide underneath the silkscreen name label
#endif
@@ -322,12 +450,4 @@ bool TFTDisplay::connect()
return true;
}
// Get touch coords from the display
void TFTDisplay::getTouch(int *x, int *y)
{
#ifndef M5STACK
tft.getTouch(x, y);
#endif
}
#endif

View File

@@ -3,7 +3,7 @@
#include <OLEDDisplay.h>
/**
* An adapter class that allows using the TFT_eSPI library as if it was an OLEDDisplay implementation.
* An adapter class that allows using the LovyanGFX library as if it was an OLEDDisplay implementation.
*
* Remaining TODO:
* optimize display() to only draw changed pixels (see other OLED subclasses for examples)
@@ -22,14 +22,19 @@ class TFTDisplay : public OLEDDisplay
// Write the buffer to the display memory
virtual void display(void) override;
// Turn the display upside down
virtual void flipScreenVertically();
// Touch screen (static handlers)
static bool hasTouch(void);
static bool getTouch(int16_t *x, int16_t *y);
/**
* shim to make the abstraction happy
*
*/
void setDetected(uint8_t detected);
void getTouch(int *x, int *y);
protected:
// the header size of the buffer used, e.g. for the SPI command header
virtual int getBufferOffset(void) override { return 0; }

View File

@@ -14,7 +14,8 @@ const uint8_t imgUser[] PROGMEM = {0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3
const uint8_t imgPositionEmpty[] PROGMEM = {0x20, 0x30, 0x28, 0x24, 0x42, 0xFF};
const uint8_t imgPositionSolid[] PROGMEM = {0x20, 0x30, 0x38, 0x3C, 0x7E, 0xFF};
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
#if (defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)) && \
!defined(DISPLAY_FORCE_SMALL_FONTS)
const uint8_t imgQuestionL1[] PROGMEM = {0xff, 0x01, 0x01, 0x32, 0x7b, 0x49, 0x49, 0x6f, 0x26, 0x01, 0x01, 0xff};
const uint8_t imgQuestionL2[] PROGMEM = {0x0f, 0x08, 0x08, 0x08, 0x06, 0x0f, 0x0f, 0x06, 0x08, 0x08, 0x08, 0x0f};
const uint8_t imgInfoL1[] PROGMEM = {0xff, 0x01, 0x01, 0x01, 0x1e, 0x7f, 0x1e, 0x01, 0x01, 0x01, 0x01, 0xff};

181
src/input/BBQ10Keyboard.cpp Normal file
View File

@@ -0,0 +1,181 @@
// Based on arturo182 arduino_bbq10kbd library https://github.com/arturo182/arduino_bbq10kbd
#include <Arduino.h>
#include "BBQ10Keyboard.h"
#define _REG_VER 1
#define _REG_CFG 2
#define _REG_INT 3
#define _REG_KEY 4
#define _REG_BKL 5
#define _REG_DEB 6
#define _REG_FRQ 7
#define _REG_RST 8
#define _REG_FIF 9
#define _WRITE_MASK (1 << 7)
#define CFG_OVERFLOW_ON (1 << 0)
#define CFG_OVERFLOW_INT (1 << 1)
#define CFG_CAPSLOCK_INT (1 << 2)
#define CFG_NUMLOCK_INT (1 << 3)
#define CFG_KEY_INT (1 << 4)
#define CFG_PANIC_INT (1 << 5)
#define CFG_REPORT_MODS (1 << 6)
#define CFG_USE_MODS (1 << 7)
#define INT_OVERFLOW (1 << 0)
#define INT_CAPSLOCK (1 << 1)
#define INT_NUMLOCK (1 << 2)
#define INT_KEY (1 << 3)
#define INT_PANIC (1 << 4)
#define KEY_CAPSLOCK (1 << 5)
#define KEY_NUMLOCK (1 << 6)
#define KEY_COUNT_MASK (0x1F)
BBQ10Keyboard::BBQ10Keyboard() : m_wire(nullptr), m_addr(0), readCallback(nullptr), writeCallback(nullptr) {}
void BBQ10Keyboard::begin(uint8_t addr, TwoWire *wire)
{
m_addr = addr;
m_wire = wire;
m_wire->begin();
reset();
}
void BBQ10Keyboard::begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr)
{
m_addr = addr;
m_wire = nullptr;
writeCallback = w;
readCallback = r;
reset();
}
void BBQ10Keyboard::reset()
{
if (m_wire) {
m_wire->beginTransmission(m_addr);
m_wire->write(_REG_RST);
m_wire->endTransmission();
}
if (writeCallback) {
uint8_t data = 0;
writeCallback(m_addr, _REG_RST, &data, 0);
}
delay(100);
writeRegister(_REG_CFG, readRegister8(_REG_CFG) | CFG_REPORT_MODS);
delay(100);
}
void BBQ10Keyboard::attachInterrupt(uint8_t pin, void (*func)(void)) const
{
pinMode(pin, INPUT_PULLUP);
::attachInterrupt(digitalPinToInterrupt(pin), func, RISING);
}
void BBQ10Keyboard::detachInterrupt(uint8_t pin) const
{
::detachInterrupt(pin);
}
void BBQ10Keyboard::clearInterruptStatus()
{
writeRegister(_REG_INT, 0x00);
}
uint8_t BBQ10Keyboard::status() const
{
return readRegister8(_REG_KEY);
}
uint8_t BBQ10Keyboard::keyCount() const
{
return status() & KEY_COUNT_MASK;
}
BBQ10Keyboard::KeyEvent BBQ10Keyboard::keyEvent() const
{
KeyEvent event = {.key = '\0', .state = StateIdle};
if (keyCount() == 0)
return event;
const uint16_t buf = readRegister16(_REG_FIF);
event.key = buf >> 8;
event.state = KeyState(buf & 0xFF);
return event;
}
float BBQ10Keyboard::backlight() const
{
return readRegister8(_REG_BKL) / 255.0f;
}
void BBQ10Keyboard::setBacklight(float value)
{
writeRegister(_REG_BKL, value * 255);
}
uint8_t BBQ10Keyboard::readRegister8(uint8_t reg) const
{
if (m_wire) {
m_wire->beginTransmission(m_addr);
m_wire->write(reg);
m_wire->endTransmission();
m_wire->requestFrom(m_addr, (uint8_t)1);
if (m_wire->available() < 1)
return 0;
return m_wire->read();
}
if (readCallback) {
uint8_t data;
readCallback(m_addr, reg, &data, 1);
return data;
}
return 0;
}
uint16_t BBQ10Keyboard::readRegister16(uint8_t reg) const
{
uint8_t data[2] = {0};
// uint8_t low = 0, high = 0;
if (m_wire) {
m_wire->beginTransmission(m_addr);
m_wire->write(reg);
m_wire->endTransmission();
m_wire->requestFrom(m_addr, (uint8_t)2);
if (m_wire->available() < 2)
return 0;
data[0] = m_wire->read();
data[1] = m_wire->read();
}
if (readCallback) {
readCallback(m_addr, reg, data, 2);
}
return (data[1] << 8) | data[0];
}
void BBQ10Keyboard::writeRegister(uint8_t reg, uint8_t value)
{
uint8_t data[2];
data[0] = reg | _WRITE_MASK;
data[1] = value;
if (m_wire) {
m_wire->beginTransmission(m_addr);
m_wire->write(data, sizeof(uint8_t) * 2);
m_wire->endTransmission();
}
if (writeCallback) {
writeCallback(m_addr, data[0], &(data[1]), 1);
}
}

51
src/input/BBQ10Keyboard.h Normal file
View File

@@ -0,0 +1,51 @@
// Based on arturo182 arduino_bbq10kbd library https://github.com/arturo182/arduino_bbq10kbd
#include "configuration.h"
#include <Wire.h>
#define KEY_MOD_ALT (0x1A)
#define KEY_MOD_SHL (0x1B)
#define KEY_MOD_SHR (0x1C)
#define KEY_MOD_SYM (0x1D)
class BBQ10Keyboard
{
public:
typedef uint8_t (*i2c_com_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t len);
enum KeyState { StateIdle = 0, StatePress, StateLongPress, StateRelease };
struct KeyEvent {
char key;
KeyState state;
};
BBQ10Keyboard();
void begin(uint8_t addr = BBQ10_KB_ADDR, TwoWire *wire = &Wire);
void begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr = BBQ10_KB_ADDR);
void reset(void);
void attachInterrupt(uint8_t pin, void (*func)(void)) const;
void detachInterrupt(uint8_t pin) const;
void clearInterruptStatus(void);
uint8_t status(void) const;
uint8_t keyCount(void) const;
KeyEvent keyEvent(void) const;
float backlight() const;
void setBacklight(float value);
uint8_t readRegister8(uint8_t reg) const;
uint16_t readRegister16(uint8_t reg) const;
void writeRegister(uint8_t reg, uint8_t value);
private:
TwoWire *m_wire;
uint8_t m_addr;
i2c_com_fptr_t readCallback;
i2c_com_fptr_t writeCallback;
};

View File

@@ -0,0 +1,137 @@
#include "TouchScreenBase.h"
#include "main.h"
#ifndef TIME_LONG_PRESS
#define TIME_LONG_PRESS 400
#endif
// move a minimum distance over the screen to detect a "swipe"
#ifndef TOUCH_THRESHOLD_X
#define TOUCH_THRESHOLD_X 30
#endif
#ifndef TOUCH_THRESHOLD_Y
#define TOUCH_THRESHOLD_Y 20
#endif
TouchScreenBase::TouchScreenBase(const char *name, uint16_t width, uint16_t height)
: concurrency::OSThread(name), _display_width(width), _display_height(height), _first_x(0), _last_x(0), _first_y(0),
_last_y(0), _start(0), _tapped(false), _originName(name)
{
}
void TouchScreenBase::init(bool hasTouch)
{
if (hasTouch) {
LOG_INFO("TouchScreen initialized %d %d\n", TOUCH_THRESHOLD_X, TOUCH_THRESHOLD_Y);
this->setInterval(100);
} else {
disable();
this->setInterval(UINT_MAX);
}
}
int32_t TouchScreenBase::runOnce()
{
TouchEvent e;
e.touchEvent = static_cast<char>(TOUCH_ACTION_NONE);
// process touch events
int16_t x, y;
bool touched = getTouch(x, y);
if (touched) {
this->setInterval(20);
_last_x = x;
_last_y = y;
}
if (touched != _touchedOld) {
if (touched) {
hapticFeedback();
_state = TOUCH_EVENT_OCCURRED;
_start = millis();
_first_x = x;
_first_y = y;
} else {
_state = TOUCH_EVENT_CLEARED;
time_t duration = millis() - _start;
x = _last_x;
y = _last_y;
this->setInterval(50);
// compute distance
int16_t dx = x - _first_x;
int16_t dy = y - _first_y;
uint16_t adx = abs(dx);
uint16_t ady = abs(dy);
// swipe horizontal
if (adx > ady && adx > TOUCH_THRESHOLD_X) {
if (0 > dx) { // swipe right to left
e.touchEvent = static_cast<char>(TOUCH_ACTION_LEFT);
LOG_DEBUG("action SWIPE: right to left\n");
} else { // swipe left to right
e.touchEvent = static_cast<char>(TOUCH_ACTION_RIGHT);
LOG_DEBUG("action SWIPE: left to right\n");
}
}
// swipe vertical
else if (ady > adx && ady > TOUCH_THRESHOLD_Y) {
if (0 > dy) { // swipe bottom to top
e.touchEvent = static_cast<char>(TOUCH_ACTION_UP);
LOG_DEBUG("action SWIPE: bottom to top\n");
} else { // swipe top to bottom
e.touchEvent = static_cast<char>(TOUCH_ACTION_DOWN);
LOG_DEBUG("action SWIPE: top to bottom\n");
}
}
// tap
else {
if (duration > 0 && duration < TIME_LONG_PRESS) {
if (_tapped) {
_tapped = false;
e.touchEvent = static_cast<char>(TOUCH_ACTION_DOUBLE_TAP);
LOG_DEBUG("action DOUBLE TAP(%d/%d)\n", x, y);
} else {
_tapped = true;
}
} else {
_tapped = false;
}
}
}
}
_touchedOld = touched;
// fire TAP event when no 2nd tap occured within time
if (_tapped && (time_t(millis()) - _start) > TIME_LONG_PRESS - 50) {
_tapped = false;
e.touchEvent = static_cast<char>(TOUCH_ACTION_TAP);
LOG_DEBUG("action TAP(%d/%d)\n", _last_x, _last_y);
}
// fire LONG_PRESS event without the need for release
if (touched && (time_t(millis()) - _start) > TIME_LONG_PRESS) {
// tricky: prevent reoccurring events and another touch event when releasing
_start = millis() + 30000;
e.touchEvent = static_cast<char>(TOUCH_ACTION_LONG_PRESS);
LOG_DEBUG("action LONG PRESS(%d/%d)\n", _last_x, _last_y);
}
if (e.touchEvent != TOUCH_ACTION_NONE) {
e.source = this->_originName;
e.x = _last_x;
e.y = _last_y;
onEvent(e);
}
return interval;
}
void TouchScreenBase::hapticFeedback()
{
#ifdef T_WATCH_S3
drv.setWaveform(0, 75);
drv.setWaveform(1, 0); // end waveform
drv.go();
#endif
}

View File

@@ -0,0 +1,55 @@
#pragma once
#include "InputBroker.h"
#include "concurrency/OSThread.h"
#include "mesh/NodeDB.h"
typedef struct _TouchEvent {
const char *source;
char touchEvent;
uint16_t x;
uint16_t y;
} TouchEvent;
class TouchScreenBase : public Observable<const InputEvent *>, public concurrency::OSThread
{
public:
explicit TouchScreenBase(const char *name, uint16_t width, uint16_t height);
void init(bool hasTouch);
protected:
enum TouchScreenBaseStateType { TOUCH_EVENT_OCCURRED, TOUCH_EVENT_CLEARED };
enum TouchScreenBaseEventType {
TOUCH_ACTION_NONE,
TOUCH_ACTION_UP,
TOUCH_ACTION_DOWN,
TOUCH_ACTION_LEFT,
TOUCH_ACTION_RIGHT,
TOUCH_ACTION_TAP,
TOUCH_ACTION_DOUBLE_TAP,
TOUCH_ACTION_LONG_PRESS
};
virtual int32_t runOnce() override;
virtual bool getTouch(int16_t &x, int16_t &y) = 0;
virtual void onEvent(const TouchEvent &event) = 0;
volatile TouchScreenBaseStateType _state = TOUCH_EVENT_CLEARED;
volatile TouchScreenBaseEventType _action = TOUCH_ACTION_NONE;
void hapticFeedback();
protected:
uint16_t _display_width;
uint16_t _display_height;
private:
bool _touchedOld = false; // previous touch state
int16_t _first_x, _last_x; // horizontal swipe direction
int16_t _first_y, _last_y; // vertical swipe direction
time_t _start; // for LONG_PRESS
bool _tapped; // for DOUBLE_TAP
const char *_originName;
};

View File

@@ -0,0 +1,73 @@
#include "TouchScreenImpl1.h"
#include "InputBroker.h"
#include "PowerFSM.h"
#include "configuration.h"
TouchScreenImpl1 *touchScreenImpl1;
TouchScreenImpl1::TouchScreenImpl1(uint16_t width, uint16_t height, bool (*getTouch)(int16_t *, int16_t *))
: TouchScreenBase("touchscreen1", width, height), _getTouch(getTouch)
{
}
void TouchScreenImpl1::init()
{
#if !HAS_TOUCHSCREEN
TouchScreenBase::init(false);
return;
#else
TouchScreenBase::init(true);
inputBroker->registerSource(this);
#endif
}
bool TouchScreenImpl1::getTouch(int16_t &x, int16_t &y)
{
return _getTouch(&x, &y);
}
/**
* @brief forward touchscreen event
*
* @param event
*
* The touchscreen events are translated to input events and reversed
*/
void TouchScreenImpl1::onEvent(const TouchEvent &event)
{
InputEvent e;
e.source = event.source;
switch (event.touchEvent) {
case TOUCH_ACTION_LEFT: {
e.inputEvent = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT);
break;
}
case TOUCH_ACTION_RIGHT: {
e.inputEvent = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT);
break;
}
case TOUCH_ACTION_UP: {
e.inputEvent = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP);
break;
}
case TOUCH_ACTION_DOWN: {
e.inputEvent = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN);
break;
}
case TOUCH_ACTION_DOUBLE_TAP: {
e.inputEvent = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT);
break;
}
case TOUCH_ACTION_LONG_PRESS: {
e.inputEvent = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL);
break;
}
case TOUCH_ACTION_TAP: {
powerFSM.trigger(EVENT_INPUT);
break;
}
default:
return;
}
this->notifyObservers(&e);
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include "TouchScreenBase.h"
class TouchScreenImpl1 : public TouchScreenBase
{
public:
TouchScreenImpl1(uint16_t width, uint16_t height, bool (*getTouch)(int16_t *, int16_t *));
void init(void);
protected:
virtual bool getTouch(int16_t &x, int16_t &y);
virtual void onEvent(const TouchEvent &event);
bool (*_getTouch)(int16_t *, int16_t *);
};
extern TouchScreenImpl1 *touchScreenImpl1;

View File

@@ -0,0 +1,95 @@
#include "TrackballInterruptBase.h"
#include "configuration.h"
TrackballInterruptBase::TrackballInterruptBase(const char *name) : concurrency::OSThread(name), _originName(name) {}
void TrackballInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLeft, uint8_t pinRight, uint8_t pinPress,
char eventDown, char eventUp, char eventLeft, char eventRight, char eventPressed,
void (*onIntDown)(), void (*onIntUp)(), void (*onIntLeft)(), void (*onIntRight)(),
void (*onIntPress)())
{
this->_pinDown = pinDown;
this->_pinUp = pinUp;
this->_pinLeft = pinLeft;
this->_pinRight = pinRight;
this->_eventDown = eventDown;
this->_eventUp = eventUp;
this->_eventLeft = eventLeft;
this->_eventRight = eventRight;
this->_eventPressed = eventPressed;
pinMode(pinPress, INPUT_PULLUP);
pinMode(this->_pinDown, INPUT_PULLUP);
pinMode(this->_pinUp, INPUT_PULLUP);
pinMode(this->_pinLeft, INPUT_PULLUP);
pinMode(this->_pinRight, INPUT_PULLUP);
attachInterrupt(pinPress, onIntPress, RISING);
attachInterrupt(this->_pinDown, onIntDown, RISING);
attachInterrupt(this->_pinUp, onIntUp, RISING);
attachInterrupt(this->_pinLeft, onIntLeft, RISING);
attachInterrupt(this->_pinRight, onIntRight, RISING);
LOG_DEBUG("Trackball GPIO initialized (%d, %d, %d, %d, %d)\n", this->_pinUp, this->_pinDown, this->_pinLeft, this->_pinRight,
pinPress);
this->setInterval(100);
}
int32_t TrackballInterruptBase::runOnce()
{
InputEvent e;
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
if (this->action == TB_ACTION_PRESSED) {
// LOG_DEBUG("Trackball event Press\n");
e.inputEvent = this->_eventPressed;
} else if (this->action == TB_ACTION_UP) {
// LOG_DEBUG("Trackball event UP\n");
e.inputEvent = this->_eventUp;
} else if (this->action == TB_ACTION_DOWN) {
// LOG_DEBUG("Trackball event DOWN\n");
e.inputEvent = this->_eventDown;
} else if (this->action == TB_ACTION_LEFT) {
// LOG_DEBUG("Trackball event LEFT\n");
e.inputEvent = this->_eventLeft;
} else if (this->action == TB_ACTION_RIGHT) {
// LOG_DEBUG("Trackball event RIGHT\n");
e.inputEvent = this->_eventRight;
}
if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
e.source = this->_originName;
e.kbchar = 0x00;
this->notifyObservers(&e);
}
this->action = TB_ACTION_NONE;
return 100;
}
void TrackballInterruptBase::intPressHandler()
{
this->action = TB_ACTION_PRESSED;
}
void TrackballInterruptBase::intDownHandler()
{
this->action = TB_ACTION_DOWN;
}
void TrackballInterruptBase::intUpHandler()
{
this->action = TB_ACTION_UP;
}
void TrackballInterruptBase::intLeftHandler()
{
this->action = TB_ACTION_LEFT;
}
void TrackballInterruptBase::intRightHandler()
{
this->action = TB_ACTION_RIGHT;
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include "InputBroker.h"
#include "mesh/NodeDB.h"
class TrackballInterruptBase : public Observable<const InputEvent *>, public concurrency::OSThread
{
public:
explicit TrackballInterruptBase(const char *name);
void init(uint8_t pinDown, uint8_t pinUp, uint8_t pinLeft, uint8_t pinRight, uint8_t pinPress, char eventDown, char eventUp,
char eventLeft, char eventRight, char eventPressed, void (*onIntDown)(), void (*onIntUp)(), void (*onIntLeft)(),
void (*onIntRight)(), void (*onIntPress)());
void intPressHandler();
void intDownHandler();
void intUpHandler();
void intLeftHandler();
void intRightHandler();
virtual int32_t runOnce() override;
protected:
enum TrackballInterruptBaseActionType {
TB_ACTION_NONE,
TB_ACTION_PRESSED,
TB_ACTION_UP,
TB_ACTION_DOWN,
TB_ACTION_LEFT,
TB_ACTION_RIGHT
};
volatile TrackballInterruptBaseActionType action = TB_ACTION_NONE;
private:
uint8_t _pinDown = 0;
uint8_t _pinUp = 0;
uint8_t _pinLeft = 0;
uint8_t _pinRight = 0;
char _eventDown = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
char _eventUp = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
char _eventLeft = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
char _eventRight = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
char _eventPressed = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
const char *_originName;
};

View File

@@ -0,0 +1,54 @@
#include "TrackballInterruptImpl1.h"
#include "InputBroker.h"
#include "configuration.h"
TrackballInterruptImpl1 *trackballInterruptImpl1;
TrackballInterruptImpl1::TrackballInterruptImpl1() : TrackballInterruptBase("trackball1") {}
void TrackballInterruptImpl1::init()
{
#if !HAS_TRACKBALL
// Input device is disabled.
return;
#else
uint8_t pinUp = TB_UP;
uint8_t pinDown = TB_DOWN;
uint8_t pinLeft = TB_LEFT;
uint8_t pinRight = TB_RIGHT;
uint8_t pinPress = TB_PRESS;
char eventDown = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN);
char eventUp = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP);
char eventLeft = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT);
char eventRight = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT);
char eventPressed = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT);
TrackballInterruptBase::init(pinDown, pinUp, pinLeft, pinRight, pinPress, eventDown, eventUp, eventLeft, eventRight,
eventPressed, TrackballInterruptImpl1::handleIntDown, TrackballInterruptImpl1::handleIntUp,
TrackballInterruptImpl1::handleIntLeft, TrackballInterruptImpl1::handleIntRight,
TrackballInterruptImpl1::handleIntPressed);
inputBroker->registerSource(this);
#endif
}
void TrackballInterruptImpl1::handleIntDown()
{
trackballInterruptImpl1->intDownHandler();
}
void TrackballInterruptImpl1::handleIntUp()
{
trackballInterruptImpl1->intUpHandler();
}
void TrackballInterruptImpl1::handleIntLeft()
{
trackballInterruptImpl1->intLeftHandler();
}
void TrackballInterruptImpl1::handleIntRight()
{
trackballInterruptImpl1->intRightHandler();
}
void TrackballInterruptImpl1::handleIntPressed()
{
trackballInterruptImpl1->intPressHandler();
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include "TrackballInterruptBase.h"
class TrackballInterruptImpl1 : public TrackballInterruptBase
{
public:
TrackballInterruptImpl1();
void init();
static void handleIntDown();
static void handleIntUp();
static void handleIntLeft();
static void handleIntRight();
static void handleIntPressed();
};
extern TrackballInterruptImpl1 *trackballInterruptImpl1;

View File

@@ -23,7 +23,7 @@ void UpDownInterruptBase::init(uint8_t pinDown, uint8_t pinUp, uint8_t pinPress,
attachInterrupt(this->_pinDown, onIntDown, RISING);
attachInterrupt(this->_pinUp, onIntUp, RISING);
LOG_DEBUG("GPIO initialized (%d, %d, %d)\n", this->_pinDown, this->_pinUp, pinPress);
LOG_DEBUG("Up/down/press GPIO initialized (%d, %d, %d)\n", this->_pinUp, this->_pinDown, pinPress);
}
void UpDownInterruptBase::intPressHandler()

View File

@@ -7,7 +7,7 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {}
void CardKbI2cImpl::init()
{
if (cardkb_found.address != CARDKB_ADDR) {
if (cardkb_found.address == 0x00) {
disable();
return;
}

View File

@@ -28,20 +28,9 @@ uint8_t read_from_14004(TwoWire *i2cBus, uint8_t reg, uint8_t *data, uint8_t len
return readflag;
}
// Unused for now - flagging it off
#if 0
void write_to_14004(const TwoWire * i2cBus, uint8_t reg, uint8_t data)
{
i2cBus->beginTransmission(CARDKB_ADDR);
i2cBus->write(reg);
i2cBus->write(data);
i2cBus->endTransmission(); // stop transmitting
}
#endif
int32_t KbI2cBase::runOnce()
{
if (cardkb_found.address != CARDKB_ADDR) {
if (cardkb_found.address == 0x00) {
// Input device is not detected.
return INT32_MAX;
}
@@ -52,11 +41,19 @@ int32_t KbI2cBase::runOnce()
#ifdef I2C_SDA1
LOG_DEBUG("Using I2C Bus 1 (the second one)\n");
i2cBus = &Wire1;
if (cardkb_found.address == BBQ10_KB_ADDR) {
Q10keyboard.begin(BBQ10_KB_ADDR, &Wire1);
Q10keyboard.setBacklight(0);
}
break;
#endif
case ScanI2C::WIRE:
LOG_DEBUG("Using I2C Bus 0 (the first one)\n");
i2cBus = &Wire;
if (cardkb_found.address == BBQ10_KB_ADDR) {
Q10keyboard.begin(BBQ10_KB_ADDR, &Wire);
Q10keyboard.setBacklight(0);
}
break;
case ScanI2C::NO_I2C:
default:
@@ -64,7 +61,105 @@ int32_t KbI2cBase::runOnce()
}
}
if (kb_model == 0x02) {
switch (kb_model) {
case 0x11: { // BB Q10
int keyCount = Q10keyboard.keyCount();
while (keyCount--) {
const BBQ10Keyboard::KeyEvent key = Q10keyboard.keyEvent();
if ((key.key != 0x00) && (key.state == BBQ10Keyboard::StateRelease)) {
InputEvent e;
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
e.source = this->_originName;
switch (key.key) {
case 'p': // TAB
case 't': // TAB as well
if (is_sym) {
e.inputEvent = ANYKEY;
e.kbchar = 0x09; // TAB Scancode
is_sym = false; // reset sym state after second keypress
} else {
e.inputEvent = ANYKEY;
e.kbchar = key.key;
}
break;
case 'q': // ESC
if (is_sym) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
e.kbchar = 0x1b;
is_sym = false; // reset sym state after second keypress
} else {
e.inputEvent = ANYKEY;
e.kbchar = key.key;
}
break;
case 0x08: // Back
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
e.kbchar = key.key;
break;
case 'e': // sym e
if (is_sym) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
e.kbchar = 0xb5;
is_sym = false; // reset sym state after second keypress
} else {
e.inputEvent = ANYKEY;
e.kbchar = key.key;
}
break;
case 'x': // sym x
if (is_sym) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
e.kbchar = 0xb6;
is_sym = false; // reset sym state after second keypress
} else {
e.inputEvent = ANYKEY;
e.kbchar = key.key;
}
break;
case 's': // sym s
if (is_sym) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
e.kbchar = 0x00; // tweak for destSelect
is_sym = false; // reset sym state after second keypress
} else {
e.inputEvent = ANYKEY;
e.kbchar = key.key;
}
break;
case 'f': // sym f
if (is_sym) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
e.kbchar = 0x00; // tweak for destSelect
is_sym = false; // reset sym state after second keypress
} else {
e.inputEvent = ANYKEY;
e.kbchar = key.key;
}
break;
case 0x13: // Code scanner says the SYM key is 0x13
is_sym = !is_sym;
break;
case 0x0a: // apparently Enter on Q10 is a line feed instead of carriage return
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
break;
case 0x00: // nopress
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
break;
default: // all other keys
e.inputEvent = ANYKEY;
e.kbchar = key.key;
is_sym = false; // reset sym state after second keypress
break;
}
if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
this->notifyObservers(&e);
}
}
}
break;
}
case 0x02: {
// RAK14004
uint8_t rDataBuf[8] = {0};
uint8_t PrintDataBuf = 0;
@@ -85,9 +180,12 @@ int32_t KbI2cBase::runOnce()
e.kbchar = PrintDataBuf;
this->notifyObservers(&e);
}
} else {
// m5 cardkb
i2cBus->requestFrom(CARDKB_ADDR, 1);
break;
}
case 0x00: // CARDKB
case 0x10: { // T-DECK
i2cBus->requestFrom((int)cardkb_found.address, 1);
while (i2cBus->available()) {
char c = i2cBus->read();
@@ -104,17 +202,19 @@ int32_t KbI2cBase::runOnce()
break;
case 0xb5: // Up
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
e.kbchar = 0xb5;
break;
case 0xb6: // Down
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
e.kbchar = 0xb6;
break;
case 0xb4: // Left
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
e.kbchar = c;
e.kbchar = 0xb4;
break;
case 0xb7: // Right
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
e.kbchar = c;
e.kbchar = 0xb7;
break;
case 0x0d: // Enter
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
@@ -132,6 +232,10 @@ int32_t KbI2cBase::runOnce()
this->notifyObservers(&e);
}
}
break;
}
return 500;
default:
LOG_WARN("Unknown kb_model 0x%02x\n", kb_model);
}
return 300;
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include "BBQ10Keyboard.h"
#include "InputBroker.h"
#include "Wire.h"
#include "concurrency/OSThread.h"
@@ -16,4 +17,7 @@ class KbI2cBase : public Observable<const InputEvent *>, public concurrency::OST
const char *_originName;
TwoWire *i2cBus = 0;
BBQ10Keyboard Q10keyboard;
bool is_sym = false;
};

131
src/input/kbMatrixBase.cpp Normal file
View File

@@ -0,0 +1,131 @@
#include "kbMatrixBase.h"
#include "configuration.h"
#ifdef INPUTBROKER_MATRIX_TYPE
const byte keys_cols[] = KEYS_COLS;
const byte keys_rows[] = KEYS_ROWS;
#if INPUTBROKER_MATRIX_TYPE == 1
unsigned char KeyMap[3][sizeof(keys_rows)][sizeof(keys_cols)] = {{{' ', '.', 'm', 'n', 'b', 0xb6},
{0x0d, 'l', 'k', 'j', 'h', 0xb4},
{'p', 'o', 'i', 'u', 'y', 0xb5},
{0x08, 'z', 'x', 'c', 'v', 0xb7},
{'a', 's', 'd', 'f', 'g', 0x09},
{'q', 'w', 'e', 'r', 't', 0x1a}},
{// SHIFT
{':', ';', 'M', 'N', 'B', 0xb6},
{0x0d, 'L', 'K', 'J', 'H', 0xb4},
{'P', 'O', 'I', 'U', 'Y', 0xb5},
{0x08, 'Z', 'X', 'C', 'V', 0xb7},
{'A', 'S', 'D', 'F', 'G', 0x09},
{'Q', 'W', 'E', 'R', 'T', 0x1a}},
{// SHIFT-SHIFT
{'_', ',', '>', '<', '"', '{'},
{'~', '-', '*', '&', '+', '['},
{'0', '9', '8', '7', '6', '}'},
{'=', '(', ')', '?', '/', ']'},
{'!', '@', '#', '$', '%', '\\'},
{'1', '2', '3', '4', '5', 0x1a}}};
#endif
KbMatrixBase::KbMatrixBase(const char *name) : concurrency::OSThread(name)
{
this->_originName = name;
}
int32_t KbMatrixBase::runOnce()
{
if (!INPUTBROKER_MATRIX_TYPE) {
// Input device is not requested.
return disable();
}
if (firstTime) {
// This is the first time the OSThread library has called this function, so do port setup
firstTime = 0;
for (byte i = 0; i < sizeof(keys_rows); i++) {
pinMode(keys_rows[i], OUTPUT);
digitalWrite(keys_rows[i], HIGH);
}
for (byte i = 0; i < sizeof(keys_cols); i++) {
pinMode(keys_cols[i], INPUT_PULLUP);
}
}
key = 0;
if (INPUTBROKER_MATRIX_TYPE == 1) {
// scan for keypresses
for (byte i = 0; i < sizeof(keys_rows); i++) {
digitalWrite(keys_rows[i], LOW);
for (byte j = 0; j < sizeof(keys_cols); j++) {
if (digitalRead(keys_cols[j]) == LOW) {
key = KeyMap[shift][i][j];
}
}
digitalWrite(keys_rows[i], HIGH);
}
// debounce
if (key != prevkey) {
if (key != 0) {
LOG_DEBUG("Key 0x%x pressed\n", key);
// reset shift now that we have a keypress
InputEvent e;
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
e.source = this->_originName;
switch (key) {
case 0x1b: // ESC
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
break;
case 0x08: // Back
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
e.kbchar = key;
break;
case 0xb5: // Up
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
break;
case 0xb6: // Down
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
break;
case 0xb4: // Left
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
e.kbchar = key;
break;
case 0xb7: // Right
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
e.kbchar = key;
break;
case 0x0d: // Enter
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
break;
case 0x00: // nopress
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
break;
case 0x1a: // Shift
shift++;
if (shift > 2) {
shift = 0;
}
break;
default: // all other keys
e.inputEvent = ANYKEY;
e.kbchar = key;
break;
}
if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
this->notifyObservers(&e);
}
}
prevkey = key;
}
} else {
LOG_WARN("Unknown kb_model 0x%02x\n", INPUTBROKER_MATRIX_TYPE);
return disable();
}
return 50; // Keyscan every 50msec to avoid key bounce
}
#endif // INPUTBROKER_MATRIX_TYPE

20
src/input/kbMatrixBase.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include "InputBroker.h"
#include "concurrency/OSThread.h"
class KbMatrixBase : public Observable<const InputEvent *>, public concurrency::OSThread
{
public:
explicit KbMatrixBase(const char *name);
protected:
virtual int32_t runOnce() override;
private:
const char *_originName;
bool firstTime = 1;
int shift = 0;
char key = 0;
char prevkey = 0;
};

View File

@@ -0,0 +1,20 @@
#include "kbMatrixImpl.h"
#include "InputBroker.h"
#ifdef INPUTBROKER_MATRIX_TYPE
KbMatrixImpl *kbMatrixImpl;
KbMatrixImpl::KbMatrixImpl() : KbMatrixBase("matrixKB") {}
void KbMatrixImpl::init()
{
if (!INPUTBROKER_MATRIX_TYPE) {
disable();
return;
}
inputBroker->registerSource(this);
}
#endif // INPUTBROKER_MATRIX_TYPE

19
src/input/kbMatrixImpl.h Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include "kbMatrixBase.h"
#include "main.h"
/**
* @brief The idea behind this class to have static methods for the event handlers.
* Check attachInterrupt() at RotaryEncoderInteruptBase.cpp
* Technically you can have as many rotary encoders hardver attached
* to your device as you wish, but you always need to have separate event
* handlers, thus you need to have a RotaryEncoderInterrupt implementation.
*/
class KbMatrixImpl : public KbMatrixBase
{
public:
KbMatrixImpl();
void init();
};
extern KbMatrixImpl *kbMatrixImpl;

View File

@@ -96,7 +96,7 @@ ScanI2C::DeviceAddress screen_found = ScanI2C::ADDRESS_NONE;
// The I2C address of the cardkb or RAK14004 (if found)
ScanI2C::DeviceAddress cardkb_found = ScanI2C::ADDRESS_NONE;
// 0x02 for RAK14004 and 0x00 for cardkb
// 0x02 for RAK14004, 0x00 for cardkb, 0x10 for T-Deck
uint8_t kb_model;
// The I2C address of the RTC Module (if found)
@@ -300,6 +300,15 @@ void setup()
#endif
#endif
#ifdef T_DECK
// enable keyboard
pinMode(KB_POWERON, OUTPUT);
digitalWrite(KB_POWERON, HIGH);
// There needs to be a delay after power on, give LILYGO-KEYBOARD some startup time
// otherwise keyboard and touch screen will not work
delay(800);
#endif
// Currently only the tbeam has a PMU
// PMU initialization needs to be placed before i2c scanning
power = new Power();
@@ -372,8 +381,19 @@ void setup()
kb_model = 0x02;
break;
case ScanI2C::DeviceType::CARDKB:
kb_model = 0x00;
break;
case ScanI2C::DeviceType::TDECKKB:
// assign an arbitrary value to distinguish from other models
kb_model = 0x10;
break;
case ScanI2C::DeviceType::BBQ10KB:
// assign an arbitrary value to distinguish from other models
kb_model = 0x11;
break;
default:
// use this as default since it's also just zero
LOG_WARN("kb_info.type is unknown(0x%02x), setting kb_model=0x00\n", kb_info.type);
kb_model = 0x00;
}
}
@@ -458,8 +478,13 @@ void setup()
#ifdef ARCH_NRF52
nrf52Setup();
#endif
#ifdef ARCH_RP2040
rp2040Setup();
#endif
// We do this as early as possible because this loads preferences from flash
// but we need to do this after main cpu iniot (esp32setup), because we need the random seed set
// but we need to do this after main cpu init (esp32setup), because we need the random seed set
nodeDB.init();
// If we're taking on the repeater role, use flood router
@@ -524,6 +549,7 @@ void setup()
#else
// ESP32
SPI.begin(RF95_SCK, RF95_MISO, RF95_MOSI, RF95_NSS);
LOG_WARN("SPI.begin(SCK=%d, MISO=%d, MOSI=%d, NSS=%d)\n", RF95_SCK, RF95_MISO, RF95_MOSI, RF95_NSS);
SPI.setFrequency(4000000);
#endif

View File

@@ -68,7 +68,7 @@ extern uint32_t serialSinceMsec;
// This will suppress the current delay and instead try to run ASAP.
extern bool runASAP;
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), clearBonds();
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearBonds();
meshtastic_DeviceMetadata getDeviceMetadata();

View File

@@ -1,8 +1,21 @@
/**
* @file memGet.cpp
* @brief Implementation of MemGet class that provides functions to get memory information.
*
* This file contains the implementation of MemGet class that provides functions to get
* information about free heap, heap size, free psram and psram size. The functions are
* implemented for ESP32 and NRF52 architectures. If the platform does not have heap
* management function implemented, the functions return UINT32_MAX or 0.
*/
#include "memGet.h"
#include "configuration.h"
MemGet memGet;
/**
* Returns the amount of free heap memory in bytes.
* @return uint32_t The amount of free heap memory in bytes.
*/
uint32_t MemGet::getFreeHeap()
{
#ifdef ARCH_ESP32
@@ -15,6 +28,10 @@ uint32_t MemGet::getFreeHeap()
#endif
}
/**
* Returns the size of the heap memory in bytes.
* @return uint32_t The size of the heap memory in bytes.
*/
uint32_t MemGet::getHeapSize()
{
#ifdef ARCH_ESP32
@@ -27,6 +44,11 @@ uint32_t MemGet::getHeapSize()
#endif
}
/**
* Returns the amount of free psram memory in bytes.
*
* @return The amount of free psram memory in bytes.
*/
uint32_t MemGet::getFreePsram()
{
#ifdef ARCH_ESP32
@@ -36,6 +58,11 @@ uint32_t MemGet::getFreePsram()
#endif
}
/**
* @brief Returns the size of the PSRAM memory.
*
* @return uint32_t The size of the PSRAM memory.
*/
uint32_t MemGet::getPsramSize()
{
#ifdef ARCH_ESP32
@@ -43,4 +70,4 @@ uint32_t MemGet::getPsramSize()
#else
return 0;
#endif
}
}

View File

@@ -21,7 +21,7 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
{
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
printPacket("Ignoring incoming msg, because we've already seen it", p);
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
if (!moduleConfig.mqtt.enabled && config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT &&
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
@@ -49,10 +49,13 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
tosend->hop_limit--; // bump down the hop count
// If it is a traceRoute request, update the route that it went via me
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag && traceRouteModule &&
traceRouteModule->wantPacket(p)) {
traceRouteModule->updateRoute(tosend);
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");

View File

@@ -2,6 +2,7 @@
#include "PacketHistory.h"
#include "Router.h"
#include "modules/NeighborInfoModule.h"
#include "modules/TraceRouteModule.h"
/**
@@ -57,4 +58,4 @@ class FloodingRouter : public Router, protected PacketHistory
* Look for broadcasts we need to rebroadcast
*/
virtual void sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c) override;
};
};

View File

@@ -82,8 +82,13 @@ 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
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB.getMeshNode(mp->from)->has_user &&
nodeInfoModule) {
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 &&
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);
}
@@ -315,22 +320,22 @@ meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode()
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)
meshtastic_NodeInfoLite *node = refreshLocalMeshNode();
const meshtastic_NodeInfoLite *node = refreshLocalMeshNode();
meshtastic_Position pos = meshtastic_Position_init_default;
if (newStatus->getHasLock()) {
// load data from GPS object, will add timestamp + battery further down
pos = gps->p;
} else {
// The GPS has lost lock, if we are fixed position we should just keep using
// the old position
// The GPS has lost lock
#ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("onGPSchanged() - lost validLocation\n");
#endif
if (config.position.fixed_position) {
LOG_WARN("Using fixed position\n");
pos = ConvertToPosition(node->position);
}
}
// Used fixed position if configured regalrdless of GPS lock
if (config.position.fixed_position) {
LOG_WARN("Using fixed position\n");
pos = ConvertToPosition(node->position);
}
// Finally add a fresh timestamp and battery level reading

View File

@@ -48,6 +48,14 @@ class MeshService
uint32_t oldFromNum = 0;
public:
static bool isTextPayload(const meshtastic_MeshPacket *p)
{
if (moduleConfig.range_test.enabled && p->decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP) {
return true;
}
return p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP ||
p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP;
}
/// Called when some new packets have arrived from one of the radios
Observable<uint32_t> fromNumChanged;

View File

@@ -15,6 +15,7 @@
#include "error.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "modules/NeighborInfoModule.h"
#include <ErriezCRC32.h>
#include <pb_decode.h>
#include <pb_encode.h>
@@ -63,11 +64,7 @@ uint32_t error_address = 0;
static uint8_t ourMacAddr[6];
NodeDB::NodeDB()
: nodes(devicestate.node_db), numNodes(&devicestate.node_db_count), meshNodes(devicestate.node_db_lite),
numMeshNodes(&devicestate.node_db_lite_count)
{
}
NodeDB::NodeDB() : meshNodes(devicestate.node_db_lite), numMeshNodes(&devicestate.node_db_lite_count) {}
/**
* Most (but not always) of the time we want to treat packets 'from' the local phone (where from == 0), as if they originated on
@@ -109,7 +106,6 @@ bool NodeDB::resetRadioConfig(bool factory_reset)
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_TW;
// Enter super deep sleep soon and stay there not very long
// radioConfig.preferences.mesh_sds_timeout_secs = 10;
// radioConfig.preferences.sds_secs = 60;
}
@@ -166,7 +162,7 @@ void NodeDB::installDefaultConfig()
config.has_bluetooth = true;
config.device.rebroadcast_mode = meshtastic_Config_DeviceConfig_RebroadcastMode_ALL;
config.lora.sx126x_rx_boosted_gain = false;
config.lora.sx126x_rx_boosted_gain = true;
config.lora.tx_enabled =
true; // FIXME: maybe false in the future, and setting region to enable it. (unset region forces it off)
config.lora.override_duty_cycle = false;
@@ -194,7 +190,9 @@ void NodeDB::installDefaultConfig()
: meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN;
// for backward compat, default position flags are ALT+MSL
config.position.position_flags =
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL);
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL |
meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING |
meshtastic_Config_PositionConfig_PositionFlags_DOP);
#ifdef T_WATCH_S3
config.display.screen_on_secs = 30;
@@ -211,7 +209,6 @@ void NodeDB::initConfigIntervals()
config.position.position_broadcast_secs = default_broadcast_interval_secs;
config.power.ls_secs = default_ls_secs;
config.power.mesh_sds_timeout_secs = default_mesh_sds_timeout_secs;
config.power.min_wake_secs = default_min_wake_secs;
config.power.sds_secs = default_sds_secs;
config.power.wait_bluetooth_secs = default_wait_bluetooth_secs;
@@ -250,6 +247,14 @@ void NodeDB::installDefaultModuleConfig()
strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(moduleConfig.mqtt.username));
strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(moduleConfig.mqtt.password));
moduleConfig.has_neighbor_info = true;
moduleConfig.neighbor_info.enabled = false;
moduleConfig.has_detection_sensor = true;
moduleConfig.detection_sensor.enabled = false;
moduleConfig.detection_sensor.detection_triggered_high = true;
moduleConfig.detection_sensor.minimum_broadcast_secs = 45;
initModuleConfigIntervals();
}
@@ -273,6 +278,7 @@ void NodeDB::initModuleConfigIntervals()
moduleConfig.telemetry.device_update_interval = default_broadcast_interval_secs;
moduleConfig.telemetry.environment_update_interval = default_broadcast_interval_secs;
moduleConfig.telemetry.air_quality_interval = default_broadcast_interval_secs;
moduleConfig.neighbor_info.update_interval = default_broadcast_interval_secs;
}
void NodeDB::installDefaultChannels()
@@ -284,12 +290,11 @@ void NodeDB::installDefaultChannels()
void NodeDB::resetNodes()
{
devicestate.node_db_count = 0;
memset(devicestate.node_db, 0, sizeof(devicestate.node_db));
devicestate.node_db_lite_count = 0;
memset(devicestate.node_db_lite, 0, sizeof(devicestate.node_db_lite));
saveDeviceStateToDisk();
if (neighborInfoModule && moduleConfig.neighbor_info.enabled)
neighborInfoModule->resetNeighbors();
}
void NodeDB::installDefaultDeviceState()
@@ -297,12 +302,11 @@ void NodeDB::installDefaultDeviceState()
LOG_INFO("Installing default DeviceState\n");
memset(&devicestate, 0, sizeof(meshtastic_DeviceState));
*numNodes = 0;
*numMeshNodes = 0;
// init our devicestate with valid flags so protobuf writing/reading will work
devicestate.has_my_node = true;
devicestate.has_owner = true;
devicestate.node_db_count = 0;
devicestate.node_db_lite_count = 0;
devicestate.version = DEVICESTATE_CUR_VER;
devicestate.receive_queue_count = 0; // Not yet implemented FIXME
@@ -330,11 +334,9 @@ void NodeDB::init()
int saveWhat = 0;
// likewise - we always want the app requirements to come from the running appload
myNodeInfo.min_app_version = 20300; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
myNodeInfo.max_channels = MAX_NUM_CHANNELS; // tell others the max # of channels we can understand
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)
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
pickNewNodeNum();
// Set our board type so we can share it with others
@@ -345,19 +347,6 @@ void NodeDB::init()
info->user = owner;
info->has_user = true;
if (*numNodes > 0) {
LOG_DEBUG("Legacy NodeDB detected... Migrating to NodeDBLite\n");
uint32_t readIndex = 0;
const meshtastic_NodeInfo *oldNodeInfo = nodeDB.readNextNodeInfo(readIndex);
while (oldNodeInfo != NULL) {
migrateToNodeInfoLite(oldNodeInfo);
oldNodeInfo = nodeDB.readNextNodeInfo(readIndex);
}
LOG_DEBUG("Migration complete! Clearing out legacy NodeDB...\n");
devicestate.node_db_count = 0;
memset(devicestate.node_db, 0, sizeof(devicestate.node_db));
}
#ifdef ARCH_ESP32
Preferences preferences;
preferences.begin("meshtastic", false);
@@ -367,7 +356,7 @@ void NodeDB::init()
#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, *numNodes);
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;
@@ -613,14 +602,6 @@ void NodeDB::saveToDisk(int saveWhat)
}
}
const meshtastic_NodeInfo *NodeDB::readNextNodeInfo(uint32_t &readIndex)
{
if (readIndex < *numNodes)
return &nodes[readIndex++];
else
return NULL;
}
const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex)
{
if (readIndex < *numMeshNodes)
@@ -791,24 +772,13 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
uint8_t NodeDB::getMeshNodeChannel(NodeNum n)
{
meshtastic_NodeInfoLite *info = getMeshNode(n);
const meshtastic_NodeInfoLite *info = getMeshNode(n);
if (!info) {
return 0; // defaults to PRIMARY
}
return info->channel;
}
/// Find a node in our DB, return null for missing
/// NOTE: This function might be called from an ISR
meshtastic_NodeInfo *NodeDB::getNodeInfo(NodeNum n)
{
for (int i = 0; i < *numNodes; i++)
if (nodes[i].num == n)
return &nodes[i];
return NULL;
}
/// Find a node in our DB, return null for missing
/// NOTE: This function might be called from an ISR
meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n)
@@ -827,7 +797,8 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
if (!lite) {
if ((*numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
screen->print("warning: node_db_lite full! erasing oldest entry\n");
if (screen)
screen->print("warning: node_db_lite full! erasing oldest entry\n");
// look for oldest node and erase it
uint32_t oldest = UINT32_MAX;
int oldestIndex = -1;
@@ -854,57 +825,6 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
return lite;
}
void NodeDB::migrateToNodeInfoLite(const meshtastic_NodeInfo *node)
{
meshtastic_NodeInfoLite *lite = getMeshNode(node->num);
if (!lite) {
if ((*numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
screen->print("warning: node_db_lite full! erasing oldest entry\n");
// look for oldest node and erase it
uint32_t oldest = UINT32_MAX;
int oldestIndex = -1;
for (int i = 0; i < *numMeshNodes; i++) {
if (meshNodes[i].last_heard < oldest) {
oldest = meshNodes[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];
}
(*numMeshNodes)--;
}
// add the node at the end
lite = &meshNodes[(*numMeshNodes)++];
// everything is missing except the nodenum
memset(lite, 0, sizeof(*lite));
lite->num = node->num;
lite->snr = node->snr;
lite->last_heard = node->last_heard;
lite->channel = node->channel;
if (node->has_position) {
lite->has_position = true;
lite->position.latitude_i = node->position.latitude_i;
lite->position.longitude_i = node->position.longitude_i;
lite->position.altitude = node->position.altitude;
lite->position.location_source = node->position.location_source;
lite->position.time = node->position.time;
}
if (node->has_user) {
lite->has_user = true;
lite->user = node->user;
}
if (node->has_device_metrics) {
lite->has_device_metrics = true;
lite->device_metrics = node->device_metrics;
}
}
}
/// Record an error that should be reported via analytics
void recordCriticalError(meshtastic_CriticalErrorCode code, uint32_t address, const char *filename)
{

View File

@@ -19,7 +19,7 @@ DeviceState versions used to be defined in the .proto file but really only this
#define SEGMENT_DEVICESTATE 4
#define SEGMENT_CHANNELS 8
#define DEVICESTATE_CUR_VER 20
#define DEVICESTATE_CUR_VER 22
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
extern meshtastic_DeviceState devicestate;
@@ -45,9 +45,6 @@ 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_NodeInfo *nodes;
pb_size_t *numNodes;
meshtastic_NodeInfoLite *meshNodes;
pb_size_t *numMeshNodes;
@@ -137,18 +134,6 @@ class NodeDB
private:
/// Find a node in our DB, create an empty NodeInfoLite if missing
meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n);
void migrateToNodeInfoLite(const meshtastic_NodeInfo *node);
/// Find a node in our DB, return null for missing
meshtastic_NodeInfo *getNodeInfo(NodeNum n);
/// Allow the bluetooth layer to read our next nodeinfo record, or NULL if done reading
const meshtastic_NodeInfo *readNextNodeInfo(uint32_t &readIndex);
size_t getNumNodes() { return *numNodes; }
meshtastic_NodeInfo *getNodeByIndex(size_t x)
{
assert(x < *numNodes);
return &nodes[x];
}
/// Notify observers of changes to the DB
void notifyObservers(bool forceUpdate = false)
@@ -174,7 +159,6 @@ extern NodeDB nodeDB;
# prefs.position_broadcast_secs = FIXME possibly broadcast only once an hr
prefs.wait_bluetooth_secs = 1 # Don't stay in bluetooth mode
prefs.mesh_sds_timeout_secs = never
# try to stay in light sleep one full day, then briefly wake and sleep again
prefs.ls_secs = oneday
@@ -202,7 +186,6 @@ extern NodeDB nodeDB;
#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_mesh_sds_timeout_secs IF_ROUTER(NODE_DELAY_FOREVER, 2 * 60 * 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
@@ -228,10 +211,6 @@ inline uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t d
/// Sometimes we will have Position objects that only have a time, so check for
/// valid lat/lon
static inline bool hasValidPosition(const meshtastic_NodeInfo *n)
{
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
}
static inline bool hasValidPosition(const meshtastic_NodeInfoLite *n)
{
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);

View File

@@ -279,6 +279,14 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_remote_hardware_tag;
fromRadioScratch.moduleConfig.payload_variant.remote_hardware = moduleConfig.remote_hardware;
break;
case meshtastic_ModuleConfig_neighbor_info_tag:
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_neighbor_info_tag;
fromRadioScratch.moduleConfig.payload_variant.neighbor_info = moduleConfig.neighbor_info;
break;
case meshtastic_ModuleConfig_detection_sensor_tag:
fromRadioScratch.moduleConfig.which_payload_variant = meshtastic_ModuleConfig_detection_sensor_tag;
fromRadioScratch.moduleConfig.payload_variant.detection_sensor = moduleConfig.detection_sensor;
break;
default:
LOG_ERROR("Unknown module config type %d\n", config_state);
}

View File

@@ -153,7 +153,12 @@ void Router::setReceivedMessage()
meshtastic_QueueStatus Router::getQueueStatus()
{
return iface->getQueueStatus();
if (!iface) {
meshtastic_QueueStatus qs;
qs.res = qs.mesh_packet_id = qs.free = qs.maxlen = 0;
return qs;
} else
return iface->getQueueStatus();
}
ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)

View File

@@ -53,7 +53,13 @@ typedef enum _meshtastic_AdminMessage_ModuleConfigType {
/* TODO: REPLACE */
meshtastic_AdminMessage_ModuleConfigType_AUDIO_CONFIG = 7,
/* TODO: REPLACE */
meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG = 8
meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG = 8,
/* TODO: REPLACE */
meshtastic_AdminMessage_ModuleConfigType_NEIGHBORINFO_CONFIG = 9,
/* TODO: REPLACE */
meshtastic_AdminMessage_ModuleConfigType_AMBIENTLIGHTING_CONFIG = 10,
/* TODO: REPLACE */
meshtastic_AdminMessage_ModuleConfigType_DETECTIONSENSOR_CONFIG = 11
} meshtastic_AdminMessage_ModuleConfigType;
/* Struct definitions */
@@ -172,8 +178,8 @@ extern "C" {
#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_BLUETOOTH_CONFIG+1))
#define _meshtastic_AdminMessage_ModuleConfigType_MIN meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG
#define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG
#define _meshtastic_AdminMessage_ModuleConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ModuleConfigType)(meshtastic_AdminMessage_ModuleConfigType_REMOTEHARDWARE_CONFIG+1))
#define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_DETECTIONSENSOR_CONFIG
#define _meshtastic_AdminMessage_ModuleConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ModuleConfigType)(meshtastic_AdminMessage_ModuleConfigType_DETECTIONSENSOR_CONFIG+1))
#define meshtastic_AdminMessage_payload_variant_get_config_request_ENUMTYPE meshtastic_AdminMessage_ConfigType
#define meshtastic_AdminMessage_payload_variant_get_module_config_request_ENUMTYPE meshtastic_AdminMessage_ModuleConfigType

View File

@@ -51,8 +51,9 @@ typedef enum _meshtastic_Config_DeviceConfig_RebroadcastMode {
} meshtastic_Config_DeviceConfig_RebroadcastMode;
/* Bit field of boolean configuration options, indicating which optional
fields to include when assembling POSITION messages
Longitude and latitude are always included (also time if GPS-synced)
fields to include when assembling POSITION messages.
Longitude, latitude, altitude, speed, heading, and DOP
are always included (also time if GPS-synced)
NOTE: the more fields are included, the larger the message will be -
leading to longer airtime and a higher risk of packet loss */
typedef enum _meshtastic_Config_PositionConfig_PositionFlags {
@@ -269,7 +270,7 @@ typedef struct _meshtastic_Config_PositionConfig {
uint32_t tx_gpio;
/* The minimum distance in meters traveled (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled */
uint32_t broadcast_smart_minimum_distance;
/* The minumum number of seconds (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled */
/* The minimum number of seconds (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled */
uint32_t broadcast_smart_minimum_interval_secs;
} meshtastic_Config_PositionConfig;
@@ -292,11 +293,6 @@ typedef struct _meshtastic_Config_PowerConfig {
The number of seconds for to wait before turning off BLE in No Bluetooth states
0 for default of 1 minute */
uint32_t wait_bluetooth_secs;
/* Mesh Super Deep Sleep Timeout Seconds
While in Light Sleep if this value is exceeded we will lower into super deep sleep
for sds_secs (default 1 year) or a button press
0 for default of two hours, MAXUINT for disabled */
uint32_t mesh_sds_timeout_secs;
/* Super Deep Sleep Seconds
While in Light Sleep if mesh_sds_timeout_secs is exceeded we will lower into super deep sleep
for this value (default 1 year) or a button press
@@ -536,7 +532,7 @@ extern "C" {
#define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}}
#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0}
#define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0}
@@ -545,7 +541,7 @@ extern "C" {
#define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}}
#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0}
#define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0}
@@ -577,7 +573,6 @@ extern "C" {
#define meshtastic_Config_PowerConfig_on_battery_shutdown_after_secs_tag 2
#define meshtastic_Config_PowerConfig_adc_multiplier_override_tag 3
#define meshtastic_Config_PowerConfig_wait_bluetooth_secs_tag 4
#define meshtastic_Config_PowerConfig_mesh_sds_timeout_secs_tag 5
#define meshtastic_Config_PowerConfig_sds_secs_tag 6
#define meshtastic_Config_PowerConfig_ls_secs_tag 7
#define meshtastic_Config_PowerConfig_min_wake_secs_tag 8
@@ -682,7 +677,6 @@ X(a, STATIC, SINGULAR, BOOL, is_power_saving, 1) \
X(a, STATIC, SINGULAR, UINT32, on_battery_shutdown_after_secs, 2) \
X(a, STATIC, SINGULAR, FLOAT, adc_multiplier_override, 3) \
X(a, STATIC, SINGULAR, UINT32, wait_bluetooth_secs, 4) \
X(a, STATIC, SINGULAR, UINT32, mesh_sds_timeout_secs, 5) \
X(a, STATIC, SINGULAR, UINT32, sds_secs, 6) \
X(a, STATIC, SINGULAR, UINT32, ls_secs, 7) \
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 8) \
@@ -780,7 +774,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg;
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
#define meshtastic_Config_NetworkConfig_size 195
#define meshtastic_Config_PositionConfig_size 54
#define meshtastic_Config_PowerConfig_size 46
#define meshtastic_Config_PowerConfig_size 40
#define meshtastic_Config_size 198
#ifdef __cplusplus

File diff suppressed because one or more lines are too long

View File

@@ -75,6 +75,12 @@ typedef struct _meshtastic_LocalModuleConfig {
/* The part of the config that is specific to the Neighbor Info module */
bool has_neighbor_info;
meshtastic_ModuleConfig_NeighborInfoConfig neighbor_info;
/* The part of the config that is specific to the Ambient Lighting module */
bool has_ambient_lighting;
meshtastic_ModuleConfig_AmbientLightingConfig ambient_lighting;
/* The part of the config that is specific to the Detection Sensor module */
bool has_detection_sensor;
meshtastic_ModuleConfig_DetectionSensorConfig detection_sensor;
} meshtastic_LocalModuleConfig;
@@ -84,9 +90,9 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0}
#define meshtastic_LocalModuleConfig_init_default {false, meshtastic_ModuleConfig_MQTTConfig_init_default, false, meshtastic_ModuleConfig_SerialConfig_init_default, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_default, false, meshtastic_ModuleConfig_StoreForwardConfig_init_default, false, meshtastic_ModuleConfig_RangeTestConfig_init_default, false, meshtastic_ModuleConfig_TelemetryConfig_init_default, false, meshtastic_ModuleConfig_CannedMessageConfig_init_default, 0, false, meshtastic_ModuleConfig_AudioConfig_init_default, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_default, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_default}
#define meshtastic_LocalModuleConfig_init_default {false, meshtastic_ModuleConfig_MQTTConfig_init_default, false, meshtastic_ModuleConfig_SerialConfig_init_default, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_default, false, meshtastic_ModuleConfig_StoreForwardConfig_init_default, false, meshtastic_ModuleConfig_RangeTestConfig_init_default, false, meshtastic_ModuleConfig_TelemetryConfig_init_default, false, meshtastic_ModuleConfig_CannedMessageConfig_init_default, 0, false, meshtastic_ModuleConfig_AudioConfig_init_default, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_default, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_default, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_default, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_default}
#define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0}
#define meshtastic_LocalModuleConfig_init_zero {false, meshtastic_ModuleConfig_MQTTConfig_init_zero, false, meshtastic_ModuleConfig_SerialConfig_init_zero, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero, false, meshtastic_ModuleConfig_StoreForwardConfig_init_zero, false, meshtastic_ModuleConfig_RangeTestConfig_init_zero, false, meshtastic_ModuleConfig_TelemetryConfig_init_zero, false, meshtastic_ModuleConfig_CannedMessageConfig_init_zero, 0, false, meshtastic_ModuleConfig_AudioConfig_init_zero, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_zero}
#define meshtastic_LocalModuleConfig_init_zero {false, meshtastic_ModuleConfig_MQTTConfig_init_zero, false, meshtastic_ModuleConfig_SerialConfig_init_zero, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero, false, meshtastic_ModuleConfig_StoreForwardConfig_init_zero, false, meshtastic_ModuleConfig_RangeTestConfig_init_zero, false, meshtastic_ModuleConfig_TelemetryConfig_init_zero, false, meshtastic_ModuleConfig_CannedMessageConfig_init_zero, 0, false, meshtastic_ModuleConfig_AudioConfig_init_zero, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_zero, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_zero, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_zero}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_LocalConfig_device_tag 1
@@ -108,6 +114,8 @@ extern "C" {
#define meshtastic_LocalModuleConfig_audio_tag 9
#define meshtastic_LocalModuleConfig_remote_hardware_tag 10
#define meshtastic_LocalModuleConfig_neighbor_info_tag 11
#define meshtastic_LocalModuleConfig_ambient_lighting_tag 12
#define meshtastic_LocalModuleConfig_detection_sensor_tag 13
/* Struct field encoding specification for nanopb */
#define meshtastic_LocalConfig_FIELDLIST(X, a) \
@@ -140,7 +148,9 @@ X(a, STATIC, OPTIONAL, MESSAGE, canned_message, 7) \
X(a, STATIC, SINGULAR, UINT32, version, 8) \
X(a, STATIC, OPTIONAL, MESSAGE, audio, 9) \
X(a, STATIC, OPTIONAL, MESSAGE, remote_hardware, 10) \
X(a, STATIC, OPTIONAL, MESSAGE, neighbor_info, 11)
X(a, STATIC, OPTIONAL, MESSAGE, neighbor_info, 11) \
X(a, STATIC, OPTIONAL, MESSAGE, ambient_lighting, 12) \
X(a, STATIC, OPTIONAL, MESSAGE, detection_sensor, 13)
#define meshtastic_LocalModuleConfig_CALLBACK NULL
#define meshtastic_LocalModuleConfig_DEFAULT NULL
#define meshtastic_LocalModuleConfig_mqtt_MSGTYPE meshtastic_ModuleConfig_MQTTConfig
@@ -153,6 +163,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, neighbor_info, 11)
#define meshtastic_LocalModuleConfig_audio_MSGTYPE meshtastic_ModuleConfig_AudioConfig
#define meshtastic_LocalModuleConfig_remote_hardware_MSGTYPE meshtastic_ModuleConfig_RemoteHardwareConfig
#define meshtastic_LocalModuleConfig_neighbor_info_MSGTYPE meshtastic_ModuleConfig_NeighborInfoConfig
#define meshtastic_LocalModuleConfig_ambient_lighting_MSGTYPE meshtastic_ModuleConfig_AmbientLightingConfig
#define meshtastic_LocalModuleConfig_detection_sensor_MSGTYPE meshtastic_ModuleConfig_DetectionSensorConfig
extern const pb_msgdesc_t meshtastic_LocalConfig_msg;
extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
@@ -162,8 +174,8 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
#define meshtastic_LocalModuleConfig_fields &meshtastic_LocalModuleConfig_msg
/* Maximum encoded size of messages (where known) */
#define meshtastic_LocalConfig_size 461
#define meshtastic_LocalModuleConfig_size 547
#define meshtastic_LocalConfig_size 455
#define meshtastic_LocalModuleConfig_size 609
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -59,6 +59,10 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_TLORA_T3_S3 = 16,
/* B&Q Consulting Nano G1 Explorer: https://wiki.uniteng.com/en/meshtastic/nano-g1-explorer */
meshtastic_HardwareModel_NANO_G1_EXPLORER = 17,
/* B&Q Consulting Nano G2 Ultra: https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra */
meshtastic_HardwareModel_NANO_G2_ULTRA = 18,
/* LoRAType device: https://loratype.org/ */
meshtastic_HardwareModel_LORA_TYPE = 19,
/* B&Q Consulting Station Edition G1: https://uniteng.com/wiki/doku.php?id=meshtastic:station */
meshtastic_HardwareModel_STATION_G1 = 25,
/* RAK11310 (RP2040 + SX1262) */
@@ -101,10 +105,14 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_HELTEC_WIRELESS_TRACKER = 48,
/* Heltec Wireless Paper with ESP32-S3 CPU and E-Ink display */
meshtastic_HardwareModel_HELTEC_WIRELESS_PAPER = 49,
/* LilyGo T-Deck with ESP32-S3 CPU, Keyboard, and IPS display */
/* LilyGo T-Deck with ESP32-S3 CPU, Keyboard and IPS display */
meshtastic_HardwareModel_T_DECK = 50,
/* LilyGo T-Watch S3 with ESP32-S3 CPU and IPS display */
meshtastic_HardwareModel_T_WATCH_S3 = 51,
/* Bobricius Picomputer with ESP32-S3 CPU, Keyboard and IPS display */
meshtastic_HardwareModel_PICOMPUTER_S3 = 52,
/* Heltec HT-CT62 with ESP32-C3 CPU and SX1262 LoRa */
meshtastic_HardwareModel_HELTEC_HT62 = 53,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -147,7 +155,7 @@ typedef enum _meshtastic_CriticalErrorCode {
/* Radio transmit hardware failure. We sent data to the radio chip, but it didn't
reply with an interrupt. */
meshtastic_CriticalErrorCode_TRANSMIT_FAILED = 8,
/* We detected that the main CPU voltage dropped below the minumum acceptable value */
/* We detected that the main CPU voltage dropped below the minimum acceptable value */
meshtastic_CriticalErrorCode_BROWNOUT = 9,
/* Selftest of SX1262 radio chip failed */
meshtastic_CriticalErrorCode_SX1262_FAILURE = 10,
@@ -228,9 +236,9 @@ typedef enum _meshtastic_Routing_Error {
to make sure that critical packets are sent ASAP.
In the case of meshtastic that means we want to send protocol acks as soon as possible
(to prevent unneeded retransmissions), we want routing messages to be sent next,
then messages marked as reliable and finally background packets like periodic position updates.
then messages marked as reliable and finally 'background' packets like periodic position updates.
So I bit the bullet and implemented a new (internal - not sent over the air)
field in MeshPacket called priority.
field in MeshPacket called 'priority'.
And the transmission queue in the router object is now a priority queue. */
typedef enum _meshtastic_MeshPacket_Priority {
/* Treated as Priority.DEFAULT */
@@ -603,60 +611,12 @@ typedef struct _meshtastic_MyNodeInfo {
/* Tells the phone what our node number is, default starting value is
lowbyte of macaddr, but it will be fixed if that is already in use */
uint32_t my_node_num;
/* Deprecated in 2.1.x (Source from device_metadata)
Note: This flag merely means we detected a hardware GPS in our node.
Not the same as UserPreferences.location_sharing */
bool has_gps;
/* Deprecated in 2.1.x
The maximum number of 'software' channels that can be set on this node. */
uint32_t max_channels;
/* Deprecated in 2.1.x (Source from device_metadata)
0.0.5 etc... */
char firmware_version[18];
/* An error message we'd like to report back to the mothership through analytics.
It indicates a serious bug occurred on the device, the device coped with it,
but we still want to tell the devs about the bug.
This field will be cleared after the phone reads MyNodeInfo
(i.e. it will only be reported once)
a numeric error code to go with error message, zero means no error */
meshtastic_CriticalErrorCode error_code;
/* A numeric error address (nonzero if available) */
uint32_t error_address;
/* The total number of errors this node has ever encountered
(well - since the last time we discarded preferences) */
uint32_t error_count;
/* The total number of reboots this node has ever encountered
(well - since the last time we discarded preferences) */
uint32_t reboot_count;
/* Deprecated in 2.1.x
Calculated bitrate of the current channel (in Bytes Per Second) */
float bitrate;
/* Deprecated in 2.1.x
How long before we consider a message abandoned and we can clear our
caches of any messages in flight Normally quite large to handle the worst case
message delivery time, 5 minutes.
Formerly called FLOOD_EXPIRE_TIME in the device code */
uint32_t message_timeout_msec;
/* The minimum app version that can talk to this device.
Phone/PC apps should compare this to their build number and if too low tell the user they must update their app */
uint32_t min_app_version;
/* Deprecated in 2.1.x (Only used on device to keep track of utilization)
24 time windows of 1hr each with the airtime transmitted out of the device per hour. */
pb_size_t air_period_tx_count;
uint32_t air_period_tx[8];
/* Deprecated in 2.1.x (Only used on device to keep track of utilization)
24 time windows of 1hr each with the airtime of valid packets for your mesh. */
pb_size_t air_period_rx_count;
uint32_t air_period_rx[8];
/* Deprecated in 2.1.x (Source from DeviceMetadata instead)
Is the device wifi capable? */
bool has_wifi;
/* Deprecated in 2.1.x (Source from DeviceMetrics telemetry payloads)
Utilization for the current channel, including well formed TX, RX and malformed RX (aka noise). */
float channel_utilization;
/* Deprecated in 2.1.x (Source from DeviceMetrics telemetry payloads)
Percent of airtime for transmission used within the last hour. */
float air_util_tx;
} meshtastic_MyNodeInfo;
/* Debug output from the device.
@@ -727,6 +687,12 @@ typedef struct _meshtastic_Neighbor {
uint32_t node_id;
/* SNR of last heard message */
float snr;
/* Reception time (in secs since 1970) of last message that was last sent by this ID.
Note: this is for local storage only and will not be sent out over the mesh. */
uint32_t last_rx_time;
/* Broadcast interval of this neighbor (in seconds).
Note: this is for local storage only and will not be sent out over the mesh. */
uint32_t node_broadcast_interval_secs;
} meshtastic_Neighbor;
/* Full info on edges for a single node */
@@ -735,6 +701,8 @@ typedef struct _meshtastic_NeighborInfo {
uint32_t node_id;
/* Field to pass neighbor info for the next sending cycle */
uint32_t last_sent_by_id;
/* Broadcast interval of the represented node (in seconds) */
uint32_t node_broadcast_interval_secs;
/* The list of out edges from this node */
pb_size_t neighbors_count;
meshtastic_Neighbor neighbors[10];
@@ -869,7 +837,6 @@ extern "C" {
#define meshtastic_MeshPacket_delayed_ENUMTYPE meshtastic_MeshPacket_Delayed
#define meshtastic_MyNodeInfo_error_code_ENUMTYPE meshtastic_CriticalErrorCode
#define meshtastic_LogRecord_level_ENUMTYPE meshtastic_LogRecord_Level
@@ -894,14 +861,14 @@ extern "C" {
#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}
#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_MyNodeInfo_init_default {0, 0, 0, "", _meshtastic_CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 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}
#define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}}
#define meshtastic_ToRadio_init_default {0, {meshtastic_MeshPacket_init_default}}
#define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}}
#define meshtastic_NeighborInfo_init_default {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}
#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_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}
#define meshtastic_User_init_zero {"", "", "", {0}, _meshtastic_HardwareModel_MIN, 0}
@@ -912,14 +879,14 @@ extern "C" {
#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}
#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_MyNodeInfo_init_zero {0, 0, 0, "", _meshtastic_CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 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}
#define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}}
#define meshtastic_ToRadio_init_zero {0, {meshtastic_MeshPacket_init_zero}}
#define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}}
#define meshtastic_NeighborInfo_init_zero {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}
#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}
/* Field tags (for use in manual encoding/decoding) */
@@ -996,21 +963,8 @@ extern "C" {
#define meshtastic_NodeInfo_device_metrics_tag 6
#define meshtastic_NodeInfo_channel_tag 7
#define meshtastic_MyNodeInfo_my_node_num_tag 1
#define meshtastic_MyNodeInfo_has_gps_tag 2
#define meshtastic_MyNodeInfo_max_channels_tag 3
#define meshtastic_MyNodeInfo_firmware_version_tag 4
#define meshtastic_MyNodeInfo_error_code_tag 5
#define meshtastic_MyNodeInfo_error_address_tag 6
#define meshtastic_MyNodeInfo_error_count_tag 7
#define meshtastic_MyNodeInfo_reboot_count_tag 8
#define meshtastic_MyNodeInfo_bitrate_tag 9
#define meshtastic_MyNodeInfo_message_timeout_msec_tag 10
#define meshtastic_MyNodeInfo_min_app_version_tag 11
#define meshtastic_MyNodeInfo_air_period_tx_tag 12
#define meshtastic_MyNodeInfo_air_period_rx_tag 13
#define meshtastic_MyNodeInfo_has_wifi_tag 14
#define meshtastic_MyNodeInfo_channel_utilization_tag 15
#define meshtastic_MyNodeInfo_air_util_tx_tag 16
#define meshtastic_LogRecord_message_tag 1
#define meshtastic_LogRecord_time_tag 2
#define meshtastic_LogRecord_source_tag 3
@@ -1028,9 +982,12 @@ extern "C" {
#define meshtastic_Compressed_data_tag 2
#define meshtastic_Neighbor_node_id_tag 1
#define meshtastic_Neighbor_snr_tag 2
#define meshtastic_Neighbor_last_rx_time_tag 3
#define meshtastic_Neighbor_node_broadcast_interval_secs_tag 4
#define meshtastic_NeighborInfo_node_id_tag 1
#define meshtastic_NeighborInfo_last_sent_by_id_tag 2
#define meshtastic_NeighborInfo_neighbors_tag 3
#define meshtastic_NeighborInfo_node_broadcast_interval_secs_tag 3
#define meshtastic_NeighborInfo_neighbors_tag 4
#define meshtastic_DeviceMetadata_firmware_version_tag 1
#define meshtastic_DeviceMetadata_device_state_version_tag 2
#define meshtastic_DeviceMetadata_canShutdown_tag 3
@@ -1173,21 +1130,8 @@ X(a, STATIC, SINGULAR, UINT32, channel, 7)
#define meshtastic_MyNodeInfo_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, my_node_num, 1) \
X(a, STATIC, SINGULAR, BOOL, has_gps, 2) \
X(a, STATIC, SINGULAR, UINT32, max_channels, 3) \
X(a, STATIC, SINGULAR, STRING, firmware_version, 4) \
X(a, STATIC, SINGULAR, UENUM, error_code, 5) \
X(a, STATIC, SINGULAR, UINT32, error_address, 6) \
X(a, STATIC, SINGULAR, UINT32, error_count, 7) \
X(a, STATIC, SINGULAR, UINT32, reboot_count, 8) \
X(a, STATIC, SINGULAR, FLOAT, bitrate, 9) \
X(a, STATIC, SINGULAR, UINT32, message_timeout_msec, 10) \
X(a, STATIC, SINGULAR, UINT32, min_app_version, 11) \
X(a, STATIC, REPEATED, UINT32, air_period_tx, 12) \
X(a, STATIC, REPEATED, UINT32, air_period_rx, 13) \
X(a, STATIC, SINGULAR, BOOL, has_wifi, 14) \
X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 15) \
X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 16)
X(a, STATIC, SINGULAR, UINT32, min_app_version, 11)
#define meshtastic_MyNodeInfo_CALLBACK NULL
#define meshtastic_MyNodeInfo_DEFAULT NULL
@@ -1257,14 +1201,17 @@ X(a, STATIC, SINGULAR, BYTES, data, 2)
#define meshtastic_NeighborInfo_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, node_id, 1) \
X(a, STATIC, SINGULAR, UINT32, last_sent_by_id, 2) \
X(a, STATIC, REPEATED, MESSAGE, neighbors, 3)
X(a, STATIC, SINGULAR, UINT32, node_broadcast_interval_secs, 3) \
X(a, STATIC, REPEATED, MESSAGE, neighbors, 4)
#define meshtastic_NeighborInfo_CALLBACK NULL
#define meshtastic_NeighborInfo_DEFAULT NULL
#define meshtastic_NeighborInfo_neighbors_MSGTYPE meshtastic_Neighbor
#define meshtastic_Neighbor_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, node_id, 1) \
X(a, STATIC, SINGULAR, FLOAT, snr, 2)
X(a, STATIC, SINGULAR, FLOAT, snr, 2) \
X(a, STATIC, SINGULAR, FIXED32, last_rx_time, 3) \
X(a, STATIC, SINGULAR, UINT32, node_broadcast_interval_secs, 4)
#define meshtastic_Neighbor_CALLBACK NULL
#define meshtastic_Neighbor_DEFAULT NULL
@@ -1329,9 +1276,9 @@ extern const pb_msgdesc_t meshtastic_DeviceMetadata_msg;
#define meshtastic_LogRecord_size 81
#define meshtastic_MeshPacket_size 321
#define meshtastic_MqttClientProxyMessage_size 501
#define meshtastic_MyNodeInfo_size 179
#define meshtastic_NeighborInfo_size 142
#define meshtastic_Neighbor_size 11
#define meshtastic_MyNodeInfo_size 18
#define meshtastic_NeighborInfo_size 258
#define meshtastic_Neighbor_size 22
#define meshtastic_NodeInfo_size 261
#define meshtastic_Position_size 137
#define meshtastic_QueueStatus_size 23

View File

@@ -18,6 +18,9 @@ PB_BIND(meshtastic_ModuleConfig_RemoteHardwareConfig, meshtastic_ModuleConfig_Re
PB_BIND(meshtastic_ModuleConfig_NeighborInfoConfig, meshtastic_ModuleConfig_NeighborInfoConfig, AUTO)
PB_BIND(meshtastic_ModuleConfig_DetectionSensorConfig, meshtastic_ModuleConfig_DetectionSensorConfig, AUTO)
PB_BIND(meshtastic_ModuleConfig_AudioConfig, meshtastic_ModuleConfig_AudioConfig, AUTO)

View File

@@ -125,6 +125,33 @@ typedef struct _meshtastic_ModuleConfig_NeighborInfoConfig {
uint32_t update_interval;
} meshtastic_ModuleConfig_NeighborInfoConfig;
/* Detection Sensor Module Config */
typedef struct _meshtastic_ModuleConfig_DetectionSensorConfig {
/* Whether the Module is enabled */
bool enabled;
/* Interval in seconds of how often we can send a message to the mesh when a state change is detected */
uint32_t minimum_broadcast_secs;
/* Interval in seconds of how often we should send a message to the mesh with the current state regardless of changes
When set to 0, only state changes will be broadcasted
Works as a sort of status heartbeat for peace of mind */
uint32_t state_broadcast_secs;
/* Send ASCII bell with alert message
Useful for triggering ext. notification on bell */
bool send_bell;
/* Friendly name used to format message sent to mesh
Example: A name "Motion" would result in a message "Motion detected"
Maximum length of 20 characters */
char name[20];
/* GPIO pin to monitor for state changes */
uint8_t monitor_pin;
/* Whether or not the GPIO pin state detection is triggered on HIGH (1)
Otherwise LOW (0) */
bool detection_triggered_high;
/* Whether or not use INPUT_PULLUP mode for GPIO pin
Only applicable if the board uses pull-up resistors on the pin */
bool use_pullup;
} meshtastic_ModuleConfig_DetectionSensorConfig;
/* Audio Config for codec2 voice */
typedef struct _meshtastic_ModuleConfig_AudioConfig {
/* Whether Audio is enabled */
@@ -342,6 +369,8 @@ typedef struct _meshtastic_ModuleConfig {
meshtastic_ModuleConfig_NeighborInfoConfig neighbor_info;
/* TODO: REPLACE */
meshtastic_ModuleConfig_AmbientLightingConfig ambient_lighting;
/* TODO: REPLACE */
meshtastic_ModuleConfig_DetectionSensorConfig detection_sensor;
} payload_variant;
} meshtastic_ModuleConfig;
@@ -375,6 +404,7 @@ extern "C" {
#define meshtastic_ModuleConfig_AudioConfig_bitrate_ENUMTYPE meshtastic_ModuleConfig_AudioConfig_Audio_Baud
#define meshtastic_ModuleConfig_SerialConfig_baud_ENUMTYPE meshtastic_ModuleConfig_SerialConfig_Serial_Baud
@@ -397,6 +427,7 @@ extern "C" {
#define meshtastic_ModuleConfig_MQTTConfig_init_default {0, "", "", "", 0, 0, 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}
#define meshtastic_ModuleConfig_AudioConfig_init_default {0, 0, _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0}
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
@@ -410,6 +441,7 @@ extern "C" {
#define meshtastic_ModuleConfig_MQTTConfig_init_zero {0, "", "", "", 0, 0, 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}
#define meshtastic_ModuleConfig_AudioConfig_init_zero {0, 0, _meshtastic_ModuleConfig_AudioConfig_Audio_Baud_MIN, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0}
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
@@ -432,6 +464,14 @@ extern "C" {
#define meshtastic_ModuleConfig_MQTTConfig_proxy_to_client_enabled_tag 9
#define meshtastic_ModuleConfig_NeighborInfoConfig_enabled_tag 1
#define meshtastic_ModuleConfig_NeighborInfoConfig_update_interval_tag 2
#define meshtastic_ModuleConfig_DetectionSensorConfig_enabled_tag 1
#define meshtastic_ModuleConfig_DetectionSensorConfig_minimum_broadcast_secs_tag 2
#define meshtastic_ModuleConfig_DetectionSensorConfig_state_broadcast_secs_tag 3
#define meshtastic_ModuleConfig_DetectionSensorConfig_send_bell_tag 4
#define meshtastic_ModuleConfig_DetectionSensorConfig_name_tag 5
#define meshtastic_ModuleConfig_DetectionSensorConfig_monitor_pin_tag 6
#define meshtastic_ModuleConfig_DetectionSensorConfig_detection_triggered_high_tag 7
#define meshtastic_ModuleConfig_DetectionSensorConfig_use_pullup_tag 8
#define meshtastic_ModuleConfig_AudioConfig_codec2_enabled_tag 1
#define meshtastic_ModuleConfig_AudioConfig_ptt_pin_tag 2
#define meshtastic_ModuleConfig_AudioConfig_bitrate_tag 3
@@ -509,6 +549,7 @@ extern "C" {
#define meshtastic_ModuleConfig_remote_hardware_tag 9
#define meshtastic_ModuleConfig_neighbor_info_tag 10
#define meshtastic_ModuleConfig_ambient_lighting_tag 11
#define meshtastic_ModuleConfig_detection_sensor_tag 12
/* Struct field encoding specification for nanopb */
#define meshtastic_ModuleConfig_FIELDLIST(X, a) \
@@ -522,7 +563,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,canned_message,payload_varia
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,audio,payload_variant.audio), 8) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,remote_hardware,payload_variant.remote_hardware), 9) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,neighbor_info,payload_variant.neighbor_info), 10) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ambient_lighting,payload_variant.ambient_lighting), 11)
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ambient_lighting,payload_variant.ambient_lighting), 11) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,detection_sensor,payload_variant.detection_sensor), 12)
#define meshtastic_ModuleConfig_CALLBACK NULL
#define meshtastic_ModuleConfig_DEFAULT NULL
#define meshtastic_ModuleConfig_payload_variant_mqtt_MSGTYPE meshtastic_ModuleConfig_MQTTConfig
@@ -536,6 +578,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,ambient_lighting,payload_var
#define meshtastic_ModuleConfig_payload_variant_remote_hardware_MSGTYPE meshtastic_ModuleConfig_RemoteHardwareConfig
#define meshtastic_ModuleConfig_payload_variant_neighbor_info_MSGTYPE meshtastic_ModuleConfig_NeighborInfoConfig
#define meshtastic_ModuleConfig_payload_variant_ambient_lighting_MSGTYPE meshtastic_ModuleConfig_AmbientLightingConfig
#define meshtastic_ModuleConfig_payload_variant_detection_sensor_MSGTYPE meshtastic_ModuleConfig_DetectionSensorConfig
#define meshtastic_ModuleConfig_MQTTConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, enabled, 1) \
@@ -564,6 +607,18 @@ X(a, STATIC, SINGULAR, UINT32, update_interval, 2)
#define meshtastic_ModuleConfig_NeighborInfoConfig_CALLBACK NULL
#define meshtastic_ModuleConfig_NeighborInfoConfig_DEFAULT NULL
#define meshtastic_ModuleConfig_DetectionSensorConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, enabled, 1) \
X(a, STATIC, SINGULAR, UINT32, minimum_broadcast_secs, 2) \
X(a, STATIC, SINGULAR, UINT32, state_broadcast_secs, 3) \
X(a, STATIC, SINGULAR, BOOL, send_bell, 4) \
X(a, STATIC, SINGULAR, STRING, name, 5) \
X(a, STATIC, SINGULAR, UINT32, monitor_pin, 6) \
X(a, STATIC, SINGULAR, BOOL, detection_triggered_high, 7) \
X(a, STATIC, SINGULAR, BOOL, use_pullup, 8)
#define meshtastic_ModuleConfig_DetectionSensorConfig_CALLBACK NULL
#define meshtastic_ModuleConfig_DetectionSensorConfig_DEFAULT NULL
#define meshtastic_ModuleConfig_AudioConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, codec2_enabled, 1) \
X(a, STATIC, SINGULAR, UINT32, ptt_pin, 2) \
@@ -667,6 +722,7 @@ extern const pb_msgdesc_t meshtastic_ModuleConfig_msg;
extern const pb_msgdesc_t meshtastic_ModuleConfig_MQTTConfig_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;
extern const pb_msgdesc_t meshtastic_ModuleConfig_AudioConfig_msg;
extern const pb_msgdesc_t meshtastic_ModuleConfig_SerialConfig_msg;
extern const pb_msgdesc_t meshtastic_ModuleConfig_ExternalNotificationConfig_msg;
@@ -682,6 +738,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
#define meshtastic_ModuleConfig_MQTTConfig_fields &meshtastic_ModuleConfig_MQTTConfig_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
#define meshtastic_ModuleConfig_AudioConfig_fields &meshtastic_ModuleConfig_AudioConfig_msg
#define meshtastic_ModuleConfig_SerialConfig_fields &meshtastic_ModuleConfig_SerialConfig_msg
#define meshtastic_ModuleConfig_ExternalNotificationConfig_fields &meshtastic_ModuleConfig_ExternalNotificationConfig_msg
@@ -696,6 +753,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
#define meshtastic_ModuleConfig_AmbientLightingConfig_size 14
#define meshtastic_ModuleConfig_AudioConfig_size 19
#define meshtastic_ModuleConfig_CannedMessageConfig_size 49
#define meshtastic_ModuleConfig_DetectionSensorConfig_size 44
#define meshtastic_ModuleConfig_ExternalNotificationConfig_size 40
#define meshtastic_ModuleConfig_MQTTConfig_size 222
#define meshtastic_ModuleConfig_NeighborInfoConfig_size 8

View File

@@ -25,75 +25,101 @@
typedef enum _meshtastic_PortNum {
/* Deprecated: do not use in new code (formerly called OPAQUE)
A message sent from a device outside of the mesh, in a form the mesh does not understand
NOTE: This must be 0, because it is documented in IMeshService.aidl to be so */
NOTE: This must be 0, because it is documented in IMeshService.aidl to be so
ENCODING: binary undefined */
meshtastic_PortNum_UNKNOWN_APP = 0,
/* A simple UTF-8 text message, which even the little micros in the mesh
can understand and show on their screen eventually in some circumstances
even signal might send messages in this form (see below) */
even signal might send messages in this form (see below)
ENCODING: UTF-8 Plaintext (?) */
meshtastic_PortNum_TEXT_MESSAGE_APP = 1,
/* Reserved for built-in GPIO/example app.
See remote_hardware.proto/HardwareMessage for details on the message sent/received to this port number */
See remote_hardware.proto/HardwareMessage for details on the message sent/received to this port number
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](/docs/developers/protobufs/api#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](/docs/developers/protobufs/api#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](/docs/developers/protobufs/api#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](/docs/developers/protobufs/api#adminmessage) message
ENCODING: Protobuf */
meshtastic_PortNum_ADMIN_APP = 6,
/* Compressed TEXT_MESSAGE payloads. */
/* Compressed TEXT_MESSAGE payloads.
ENCODING: UTF-8 Plaintext (?) with Unishox2 Compression
NOTE: The Device Firmware converts a TEXT_MESSAGE_APP to TEXT_MESSAGE_COMPRESSED_APP if the compressed
payload is shorter. There's no need for app developers to do this themselves. Also the firmware will decompress
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](/docs/developers/protobufs/api#waypoint) message
ENCODING: Protobuf */
meshtastic_PortNum_WAYPOINT_APP = 8,
/* Audio Payloads.
Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now */
Encapsulated codec2 packets. On 2.4 GHZ Bandwidths only for now
ENCODING: codec2 audio frames
NOTE: audio frames contain a 3 byte header (0xc0 0xde 0xc2) and a one byte marker for the decompressed bitrate.
This marker comes from the 'moduleConfig.audio.bitrate' enum minus one. */
meshtastic_PortNum_AUDIO_APP = 9,
/* Payloads for clients with a network connection proxying MQTT pub/sub to the device */
meshtastic_PortNum_MQTT_CLIENT_PROXY_APP = 10,
/* Same as Text Message but originating from Detection Sensor Module. */
meshtastic_PortNum_DETECTION_SENSOR_APP = 10,
/* Provides a 'ping' service that replies to any packet it receives.
Also serves as a small example module. */
Also serves as a small example module.
ENCODING: ASCII Plaintext */
meshtastic_PortNum_REPLY_APP = 32,
/* Used for the python IP tunnel feature */
/* Used for the python IP tunnel feature
ENCODING: IP Packet. Handled by the python API, firmware ignores this one and pases on. */
meshtastic_PortNum_IP_TUNNEL_APP = 33,
/* Provides a hardware serial interface to send and receive from the Meshtastic network.
Connect to the RX/TX pins of a device with 38400 8N1. Packets received from the Meshtastic
network is forwarded to the RX pin while sending a packet to TX will go out to the Mesh network.
Maximum packet size of 240 bytes.
Module is disabled by default can be turned on by setting SERIAL_MODULE_ENABLED = 1 in SerialPlugh.cpp. */
Module is disabled by default can be turned on by setting SERIAL_MODULE_ENABLED = 1 in SerialPlugh.cpp.
ENCODING: binary undefined */
meshtastic_PortNum_SERIAL_APP = 64,
/* STORE_FORWARD_APP (Work in Progress)
Maintained by Jm Casler (MC Hamster) : jm@casler.org */
Maintained by Jm Casler (MC Hamster) : jm@casler.org
ENCODING: Protobuf */
meshtastic_PortNum_STORE_FORWARD_APP = 65,
/* Optional port for messages for the range test module. */
/* Optional port for messages for the range test module.
ENCODING: ASCII Plaintext */
meshtastic_PortNum_RANGE_TEST_APP = 66,
/* Provides a format to send and receive telemetry data from the Meshtastic network.
Maintained by Charles Crossan (crossan007) : crossan007@gmail.com */
Maintained by Charles Crossan (crossan007) : crossan007@gmail.com
ENCODING: Protobuf */
meshtastic_PortNum_TELEMETRY_APP = 67,
/* Experimental tools for estimating node position without a GPS
Maintained by Github user a-f-G-U-C (a Meshtastic contributor)
Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS */
Project files at https://github.com/a-f-G-U-C/Meshtastic-ZPS
ENCODING: arrays of int64 fields */
meshtastic_PortNum_ZPS_APP = 68,
/* Used to let multiple instances of Linux native applications communicate
as if they did using their LoRa chip.
Maintained by GitHub user GUVWAF.
Project files at https://github.com/GUVWAF/Meshtasticator */
Project files at https://github.com/GUVWAF/Meshtasticator
ENCODING: Protobuf (?) */
meshtastic_PortNum_SIMULATOR_APP = 69,
/* Provides a traceroute functionality to show the route a packet towards
a certain destination would take on the mesh. */
a certain destination would take on the mesh.
ENCODING: Protobuf */
meshtastic_PortNum_TRACEROUTE_APP = 70,
/* Aggregates edge info for the network by sending out a list of each node's neighbors */
/* Aggregates edge info for the network by sending out a list of each node's neighbors
ENCODING: Protobuf */
meshtastic_PortNum_NEIGHBORINFO_APP = 71,
/* 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)) */
meshtastic_PortNum_PRIVATE_APP = 256,
/* ATAK Forwarder Module https://github.com/paulmandal/atak-forwarder */
/* ATAK Forwarder Module https://github.com/paulmandal/atak-forwarder
ENCODING: libcotshrink */
meshtastic_PortNum_ATAK_FORWARDER = 257,
/* Currently we limit port nums to no higher than this value */
meshtastic_PortNum_MAX = 511

View File

@@ -402,7 +402,6 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
"href=/admin>admin</a>");
return;
} else {
res->setHeader("Content-Encoding", "gzip");
}
@@ -438,7 +437,6 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
file.close();
return;
} else {
LOG_ERROR("This should not have happened...\n");
res->println("ERROR: This should not have happened...");
@@ -467,7 +465,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
// first semicolon, if one exists:
size_t semicolonPos = contentType.find(";");
if (semicolonPos != std::string::npos) {
contentType = contentType.substr(0, semicolonPos);
contentType.resize(semicolonPos);
}
// Now, we can decide based on the content type:
@@ -581,7 +579,6 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
} else {
res->setHeader("Content-Type", "text/html");
res->println("<pre>");
@@ -857,4 +854,4 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
JSONValue *value = new JSONValue(jsonObjOuter);
res->print(value->Stringify().c_str());
delete value;
}
}

View File

@@ -16,7 +16,7 @@
#define MAX_RX_TOPHONE 32
/// max number of nodes allowed in the mesh
#define MAX_NUM_NODES (member_size(meshtastic_DeviceState, node_db) / member_size(meshtastic_DeviceState, node_db[0]))
#define MAX_NUM_NODES (member_size(meshtastic_DeviceState, node_db_lite) / member_size(meshtastic_DeviceState, node_db_lite[0]))
/// Max number of channels allowed
#define MAX_NUM_CHANNELS (member_size(meshtastic_ChannelFile, channels) / member_size(meshtastic_ChannelFile, channels[0]))

58
src/meshUtils.cpp Normal file
View File

@@ -0,0 +1,58 @@
#include "meshUtils.h"
#include <string.h>
/*
* Find the first occurrence of find in s, where the search is limited to the
* first slen characters of s.
* -
* Copyright (c) 2001 Mike Barcroft <mike@FreeBSD.org>
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
char *strnstr(const char *s, const char *find, size_t slen)
{
char c, sc;
size_t len;
if ((c = *find++) != '\0') {
len = strlen(find);
do {
do {
if (slen-- < 1 || (sc = *s++) == '\0')
return (NULL);
} while (sc != c);
if (len > slen)
return (NULL);
} while (strncmp(s, find, len) != 0);
s--;
}
return ((char *)s);
}

View File

@@ -4,4 +4,9 @@
template <class T> constexpr const T &clamp(const T &v, const T &lo, const T &hi)
{
return (v < lo) ? lo : (hi < v) ? hi : v;
}
}
#if (defined(ARCH_PORTDUINO) && !defined(STRNSTR))
#define STRNSTR
char *strnstr(const char *s, const char *find, size_t slen);
#endif

View File

@@ -368,6 +368,16 @@ void AdminModule::handleSetModuleConfig(const meshtastic_ModuleConfig &c)
moduleConfig.has_remote_hardware = true;
moduleConfig.remote_hardware = c.payload_variant.remote_hardware;
break;
case meshtastic_ModuleConfig_neighbor_info_tag:
LOG_INFO("Setting module config: Neighbor Info\n");
moduleConfig.has_neighbor_info = true;
moduleConfig.neighbor_info = c.payload_variant.neighbor_info;
break;
case meshtastic_ModuleConfig_detection_sensor_tag:
LOG_INFO("Setting module config: Detection Sensor\n");
moduleConfig.has_detection_sensor = true;
moduleConfig.detection_sensor = c.payload_variant.detection_sensor;
break;
}
saveChanges(SEGMENT_MODULECONFIG);
@@ -503,6 +513,16 @@ void AdminModule::handleGetModuleConfig(const meshtastic_MeshPacket &req, const
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_remote_hardware_tag;
res.get_module_config_response.payload_variant.remote_hardware = moduleConfig.remote_hardware;
break;
case meshtastic_AdminMessage_ModuleConfigType_NEIGHBORINFO_CONFIG:
LOG_INFO("Getting module config: Neighbor Info\n");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_neighbor_info_tag;
res.get_module_config_response.payload_variant.neighbor_info = moduleConfig.neighbor_info;
break;
case meshtastic_AdminMessage_ModuleConfigType_DETECTIONSENSOR_CONFIG:
LOG_INFO("Getting module config: Detection Sensor\n");
res.get_module_config_response.which_payload_variant = meshtastic_ModuleConfig_detection_sensor_tag;
res.get_module_config_response.payload_variant.detection_sensor = moduleConfig.detection_sensor;
break;
}
// NOTE: The phone app needs to know the ls_secsvalue so it can properly expect sleep behavior.
@@ -663,4 +683,4 @@ AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_AP
{
// restrict to the admin channel for rx
boundChannel = Channels::adminChannel;
}
}

View File

@@ -10,6 +10,10 @@
#include "main.h" // for cardkb_found
#ifndef INPUTBROKER_MATRIX_TYPE
#define INPUTBROKER_MATRIX_TYPE 0
#endif
#ifdef OLED_RU
#include "graphics/fonts/OLEDDisplayFontsRU.h"
#endif
@@ -18,7 +22,9 @@
#include "graphics/fonts/OLEDDisplayFontsUA.h"
#endif
#if defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7735_CS) || defined(ST7789_CS)
#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
#define FONT_MEDIUM ArialMT_Plain_24
@@ -57,9 +63,10 @@ CannedMessageModule *cannedMessageModule;
CannedMessageModule::CannedMessageModule()
: SinglePortModule("canned", meshtastic_PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("CannedMessageModule")
{
if (moduleConfig.canned_message.enabled) {
if (moduleConfig.canned_message.enabled || CANNED_MESSAGE_MODULE_ENABLE) {
this->loadProtoForModule();
if ((this->splitConfiguredMessages() <= 0) && (cardkb_found.address != CARDKB_ADDR)) {
if ((this->splitConfiguredMessages() <= 0) && (cardkb_found.address == 0x00) && !INPUTBROKER_MATRIX_TYPE &&
!CANNED_MESSAGE_MODULE_ENABLE) {
LOG_INFO("CannedMessageModule: No messages are configured. Module is disabled\n");
this->runState = CANNED_MESSAGE_RUN_STATE_DISABLED;
disable();
@@ -134,12 +141,12 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
bool validEvent = false;
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP)) {
LOG_DEBUG("Canned message event UP\n");
// LOG_DEBUG("Canned message event UP\n");
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_UP;
validEvent = true;
}
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN)) {
LOG_DEBUG("Canned message event DOWN\n");
// LOG_DEBUG("Canned message event DOWN\n");
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_DOWN;
validEvent = true;
}
@@ -163,13 +170,22 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
if ((event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK)) ||
(event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) ||
(event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))) {
LOG_DEBUG("Canned message event (%x)\n", event->kbchar);
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
// LOG_DEBUG("Canned message event (%x)\n", event->kbchar);
// tweak for left/right events generated via trackball/touch with empty kbchar
if (!event->kbchar) {
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
this->payload = 0xb4;
this->destSelect = true;
} else if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT)) {
this->payload = 0xb7;
this->destSelect = true;
}
} else {
// pass the pressed key
this->payload = event->kbchar;
this->lastTouchMillis = millis();
validEvent = true;
}
this->lastTouchMillis = millis();
validEvent = true;
}
if (event->inputEvent == static_cast<char>(ANYKEY)) {
LOG_DEBUG("Canned message event any key pressed\n");
@@ -179,6 +195,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
}
// pass the pressed key
// LOG_DEBUG("Canned message ANYKEY (%x)\n", event->kbchar);
this->payload = event->kbchar;
this->lastTouchMillis = millis();
validEvent = true;
@@ -208,9 +225,9 @@ void CannedMessageModule::sendText(NodeNum dest, const char *message, bool wantR
p->want_ack = true;
p->decoded.payload.size = strlen(message);
memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size);
if (moduleConfig.canned_message.send_bell) {
p->decoded.payload.bytes[p->decoded.payload.size - 1] = 7; // Bell character
p->decoded.payload.bytes[p->decoded.payload.size] = '\0'; // Bell character
if (moduleConfig.canned_message.send_bell && p->decoded.payload.size < meshtastic_Constants_DATA_PAYLOAD_LEN) {
p->decoded.payload.bytes[p->decoded.payload.size] = 7; // Bell character
p->decoded.payload.bytes[p->decoded.payload.size + 1] = '\0'; // Bell character
p->decoded.payload.size++;
}
@@ -221,11 +238,11 @@ void CannedMessageModule::sendText(NodeNum dest, const char *message, bool wantR
int32_t CannedMessageModule::runOnce()
{
if ((!moduleConfig.canned_message.enabled) || (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED) ||
(this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE)) {
if (((!moduleConfig.canned_message.enabled) && !CANNED_MESSAGE_MODULE_ENABLE) ||
(this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED) || (this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE)) {
return INT32_MAX;
}
LOG_DEBUG("Check status\n");
// LOG_DEBUG("Check status\n");
UIFrameEvent e = {false, true};
if (this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
// TODO: might have some feedback of sendig state
@@ -300,8 +317,7 @@ int32_t CannedMessageModule::runOnce()
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
LOG_DEBUG("MOVE DOWN (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
}
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
e.frameChanged = true;
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT || this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) {
switch (this->payload) {
case 0xb4: // left
if (this->destSelect) {
@@ -347,38 +363,50 @@ int32_t CannedMessageModule::runOnce()
}
}
break;
case 0x08: // backspace
if (this->freetext.length() > 0) {
if (this->cursor == this->freetext.length()) {
this->freetext = this->freetext.substring(0, this->freetext.length() - 1);
} else {
this->freetext = this->freetext.substring(0, this->cursor - 1) +
this->freetext.substring(this->cursor, this->freetext.length());
}
this->cursor--;
}
break;
case 0x09: // tab
if (this->destSelect) {
this->destSelect = false;
} else {
this->destSelect = true;
}
break;
default:
if (this->cursor == this->freetext.length()) {
this->freetext += this->payload;
} else {
this->freetext =
this->freetext.substring(0, this->cursor) + this->payload + this->freetext.substring(this->cursor);
}
this->cursor += 1;
if (this->freetext.length() > meshtastic_Constants_DATA_PAYLOAD_LEN) {
this->cursor = meshtastic_Constants_DATA_PAYLOAD_LEN;
this->freetext = this->freetext.substring(0, meshtastic_Constants_DATA_PAYLOAD_LEN);
}
break;
}
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
e.frameChanged = true;
switch (this->payload) {
case 0x08: // backspace
if (this->freetext.length() > 0) {
if (this->cursor == this->freetext.length()) {
this->freetext = this->freetext.substring(0, this->freetext.length() - 1);
} else {
this->freetext = this->freetext.substring(0, this->cursor - 1) +
this->freetext.substring(this->cursor, this->freetext.length());
}
this->cursor--;
}
break;
case 0x09: // tab
if (this->destSelect) {
this->destSelect = false;
} else {
this->destSelect = true;
}
break;
case 0xb4: // left
case 0xb7: // right
// already handled above
break;
default:
if (this->cursor == this->freetext.length()) {
this->freetext += this->payload;
} else {
this->freetext =
this->freetext.substring(0, this->cursor) + this->payload + this->freetext.substring(this->cursor);
}
this->cursor += 1;
uint16_t maxChars = meshtastic_Constants_DATA_PAYLOAD_LEN - (moduleConfig.canned_message.send_bell ? 1 : 0);
if (this->freetext.length() > maxChars) {
this->cursor = maxChars;
this->freetext = this->freetext.substring(0, maxChars);
}
break;
}
}
this->lastTouchMillis = millis();
this->notifyObservers(&e);
@@ -406,6 +434,11 @@ const char *CannedMessageModule::getNextMessage()
{
return this->messages[this->getNextIndex()];
}
const char *CannedMessageModule::getMessageByIndex(int index)
{
return (index >= 0 && index < this->messagesCount) ? this->messages[index] : "";
}
const char *CannedMessageModule::getNodeName(NodeNum node)
{
if (node == NODENUM_BROADCAST) {
@@ -422,7 +455,7 @@ const char *CannedMessageModule::getNodeName(NodeNum node)
bool CannedMessageModule::shouldDraw()
{
if (!moduleConfig.canned_message.enabled) {
if (!moduleConfig.canned_message.enabled && !CANNED_MESSAGE_MODULE_ENABLE) {
return false;
}
return (currentMessageIndex != -1) || (this->runState != CANNED_MESSAGE_RUN_STATE_INACTIVE);
@@ -468,7 +501,9 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
}
display->drawStringf(0 + x, 0 + y, buffer, "To: %s", cannedMessageModule->getNodeName(this->dest));
// used chars right aligned
snprintf(buffer, sizeof(buffer), "%d left", meshtastic_Constants_DATA_PAYLOAD_LEN - this->freetext.length());
uint16_t charsLeft =
meshtastic_Constants_DATA_PAYLOAD_LEN - this->freetext.length() - (moduleConfig.canned_message.send_bell ? 1 : 0);
snprintf(buffer, sizeof(buffer), "%d left", charsLeft);
display->drawString(x + display->getWidth() - display->getStringWidth(buffer), y + 0, buffer);
if (this->destSelect) {
display->drawString(x + display->getWidth() - display->getStringWidth(buffer) - 1, y + 0, buffer);
@@ -482,12 +517,31 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
display->drawStringf(0 + x, 0 + y, buffer, "To: %s", cannedMessageModule->getNodeName(this->dest));
display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL, cannedMessageModule->getPrevMessage());
display->fillRect(0 + x, 0 + y + FONT_HEIGHT_SMALL * 2, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * 2, cannedMessageModule->getCurrentMessage());
display->setColor(WHITE);
display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * 3, cannedMessageModule->getNextMessage());
int lines = (display->getHeight() / FONT_HEIGHT_SMALL) - 1;
if (lines == 3) {
// static (old) behavior for small displays
display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL, cannedMessageModule->getPrevMessage());
display->fillRect(0 + x, 0 + y + FONT_HEIGHT_SMALL * 2, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * 2, cannedMessageModule->getCurrentMessage());
display->setColor(WHITE);
display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * 3, cannedMessageModule->getNextMessage());
} else {
// use entire display height for larger displays
int topMsg = (messagesCount > lines && currentMessageIndex >= lines - 1) ? currentMessageIndex - lines + 2 : 0;
for (int i = 0; i < std::min(messagesCount, lines); i++) {
if (i == currentMessageIndex - topMsg) {
display->fillRect(0 + x, 0 + y + FONT_HEIGHT_SMALL * (i + 1), x + display->getWidth(),
y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * (i + 1), cannedMessageModule->getCurrentMessage());
display->setColor(WHITE);
} else {
display->drawString(0 + x, 0 + y + FONT_HEIGHT_SMALL * (i + 1),
cannedMessageModule->getMessageByIndex(topMsg + i));
}
}
}
}
}
}
@@ -596,4 +650,4 @@ String CannedMessageModule::drawWithCursor(String text, int cursor)
return result;
}
#endif
#endif

View File

@@ -20,6 +20,10 @@ enum cannedMessageModuleRunState {
*/
#define CANNED_MESSAGE_MODULE_MESSAGES_SIZE 800
#ifndef CANNED_MESSAGE_MODULE_ENABLE
#define CANNED_MESSAGE_MODULE_ENABLE 0
#endif
class CannedMessageModule : public SinglePortModule, public Observable<const UIFrameEvent *>, private concurrency::OSThread
{
CallbackObserver<CannedMessageModule, const InputEvent *> inputObserver =
@@ -30,6 +34,7 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
const char *getCurrentMessage();
const char *getPrevMessage();
const char *getNextMessage();
const char *getMessageByIndex(int index);
const char *getNodeName(NodeNum node);
bool shouldDraw();
void eventUp();

View File

@@ -0,0 +1,102 @@
#include "DetectionSensorModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "configuration.h"
#include "main.h"
DetectionSensorModule *detectionSensorModule;
#define GPIO_POLLING_INTERVAL 100
#define DELAYED_INTERVAL 1000
int32_t DetectionSensorModule::runOnce()
{
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
// moduleConfig.detection_sensor.enabled = true;
// moduleConfig.detection_sensor.monitor_pin = 10; // WisBlock PIR IO6
// moduleConfig.detection_sensor.monitor_pin = 21; // WisBlock RAK12013 Radar IO6
// moduleConfig.detection_sensor.minimum_broadcast_secs = 30;
// moduleConfig.detection_sensor.state_broadcast_secs = 120;
// moduleConfig.detection_sensor.detection_triggered_high = true;
// strcpy(moduleConfig.detection_sensor.name, "Motion");
if (moduleConfig.detection_sensor.enabled == false)
return disable();
if (firstTime) {
// This is the first time the OSThread library has called this function, so do some setup
firstTime = false;
if (moduleConfig.detection_sensor.monitor_pin > 0) {
pinMode(moduleConfig.detection_sensor.monitor_pin, moduleConfig.detection_sensor.use_pullup ? INPUT_PULLUP : INPUT);
} else {
LOG_WARN("Detection Sensor Module: Set to enabled but no monitor pin is set. Disabling module...\n");
return disable();
}
LOG_INFO("Detection Sensor Module: Initializing\n");
return DELAYED_INTERVAL;
}
// LOG_DEBUG("Detection Sensor Module: Current pin state: %i\n", digitalRead(moduleConfig.detection_sensor.monitor_pin));
if ((millis() - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs) &&
hasDetectionEvent()) {
sendDetectionMessage();
return DELAYED_INTERVAL;
}
// Even if we haven't detected an event, broadcast our current state to the mesh on the scheduled interval as a sort
// of heartbeat. We only do this if the minimum broadcast interval is greater than zero, otherwise we'll only broadcast state
// change detections.
else if (moduleConfig.detection_sensor.state_broadcast_secs > 0 &&
(millis() - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.detection_sensor.state_broadcast_secs)) {
sendCurrentStateMessage();
return DELAYED_INTERVAL;
}
return GPIO_POLLING_INTERVAL;
}
void DetectionSensorModule::sendDetectionMessage()
{
LOG_DEBUG("Detected event observed. Sending message\n");
char *message = new char[40];
sprintf(message, "%s detected", moduleConfig.detection_sensor.name);
meshtastic_MeshPacket *p = allocDataPacket();
p->want_ack = false;
p->decoded.payload.size = strlen(message);
memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size);
if (moduleConfig.detection_sensor.send_bell && p->decoded.payload.size < meshtastic_Constants_DATA_PAYLOAD_LEN) {
p->decoded.payload.bytes[p->decoded.payload.size] = 7; // Bell character
p->decoded.payload.bytes[p->decoded.payload.size + 1] = '\0'; // Bell character
p->decoded.payload.size++;
}
LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
lastSentToMesh = millis();
service.sendToMesh(p);
delete[] message;
}
void DetectionSensorModule::sendCurrentStateMessage()
{
char *message = new char[40];
sprintf(message, "%s state: %i", moduleConfig.detection_sensor.name, hasDetectionEvent());
meshtastic_MeshPacket *p = allocDataPacket();
p->want_ack = false;
p->decoded.payload.size = strlen(message);
memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size);
LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
lastSentToMesh = millis();
service.sendToMesh(p);
delete[] message;
}
bool DetectionSensorModule::hasDetectionEvent()
{
bool currentState = digitalRead(moduleConfig.detection_sensor.monitor_pin);
// LOG_DEBUG("Detection Sensor Module: Current state: %i\n", currentState);
return moduleConfig.detection_sensor.detection_triggered_high ? currentState : !currentState;
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "SinglePortModule.h"
class DetectionSensorModule : public SinglePortModule, private concurrency::OSThread
{
public:
DetectionSensorModule()
: SinglePortModule("detection", meshtastic_PortNum_DETECTION_SENSOR_APP), OSThread("DetectionSensorModule")
{
}
protected:
virtual int32_t runOnce() override;
private:
bool firstTime = true;
uint32_t lastSentToMesh = 0;
void sendDetectionMessage();
void sendCurrentStateMessage();
bool hasDetectionEvent();
};
extern DetectionSensorModule *detectionSensorModule;

View File

@@ -1,3 +1,18 @@
/**
* @file ExternalNotificationModule.cpp
* @brief Implementation of the ExternalNotificationModule class.
*
* This file contains the implementation of the ExternalNotificationModule class, which is responsible for handling external
* notifications such as vibration, buzzer, and LED lights. The class provides methods to turn on and off the external
* notification outputs and to play ringtones using PWM buzzer. It also includes default configurations and a runOnce() method to
* handle the module's behavior.
*
* Documentation:
* https://meshtastic.org/docs/settings/moduleconfig/external-notification
*
* @author Jm Casler & Meshtastic Team
* @date [Insert Date]
*/
#include "ExternalNotificationModule.h"
#include "MeshService.h"
#include "NodeDB.h"
@@ -113,6 +128,16 @@ int32_t ExternalNotificationModule::runOnce()
}
}
bool ExternalNotificationModule::wantPacket(const meshtastic_MeshPacket *p)
{
return MeshService::isTextPayload(p);
}
/**
* Sets the external notification on for the specified index.
*
* @param index The index of the external notification to turn on.
*/
void ExternalNotificationModule::setExternalOn(uint8_t index)
{
externalCurrentState[index] = 1;
@@ -192,8 +217,8 @@ void ExternalNotificationModule::stopNow()
}
ExternalNotificationModule::ExternalNotificationModule()
: SinglePortModule("ExternalNotificationModule", meshtastic_PortNum_TEXT_MESSAGE_APP), concurrency::OSThread(
"ExternalNotificationModule")
: SinglePortModule("ExternalNotificationModule", meshtastic_PortNum_TEXT_MESSAGE_APP),
concurrency::OSThread("ExternalNotificationModule")
{
/*
Uncomment the preferences below if you want to use the module

View File

@@ -52,6 +52,8 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
virtual int32_t runOnce() override;
virtual bool wantPacket(const meshtastic_MeshPacket *p) override;
bool isNagging = false;
virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
@@ -59,4 +61,4 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
meshtastic_AdminMessage *response) override;
};
extern ExternalNotificationModule *externalNotificationModule;
extern ExternalNotificationModule *externalNotificationModule;

View File

@@ -1,10 +1,14 @@
#include "configuration.h"
#include "input/InputBroker.h"
#include "input/RotaryEncoderInterruptImpl1.h"
#include "input/TrackballInterruptImpl1.h"
#include "input/UpDownInterruptImpl1.h"
#include "input/cardKbI2cImpl.h"
#include "input/kbMatrixImpl.h"
#include "modules/AdminModule.h"
#include "modules/CannedMessageModule.h"
#include "modules/DetectionSensorModule.h"
#include "modules/NeighborInfoModule.h"
#include "modules/NodeInfoModule.h"
#include "modules/PositionModule.h"
#include "modules/RemoteHardwareModule.h"
@@ -24,7 +28,7 @@
#include "modules/esp32/AudioModule.h"
#include "modules/esp32/StoreForwardModule.h"
#endif
#if defined(ARCH_ESP32) || defined(ARCH_NRF52)
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)
#include "modules/ExternalNotificationModule.h"
#include "modules/RangeTestModule.h"
#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(CONFIG_IDF_TARGET_ESP32S2)
@@ -46,6 +50,8 @@ void setupModules()
waypointModule = new WaypointModule();
textMessageModule = new TextMessageModule();
traceRouteModule = new TraceRouteModule();
neighborInfoModule = new NeighborInfoModule();
detectionSensorModule = new DetectionSensorModule();
// Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance
// to a global variable.
@@ -59,6 +65,14 @@ void setupModules()
upDownInterruptImpl1->init();
cardKbI2cImpl = new CardKbI2cImpl();
cardKbI2cImpl->init();
#ifdef INPUTBROKER_MATRIX_TYPE
kbMatrixImpl = new KbMatrixImpl();
kbMatrixImpl->init();
#endif // INPUTBROKER_MATRIX_TYPE
#endif // HAS_BUTTON
#if HAS_TRACKBALL
trackballInterruptImpl1 = new TrackballInterruptImpl1();
trackballInterruptImpl1->init();
#endif
#if HAS_SCREEN
cannedMessageModule = new CannedMessageModule();
@@ -81,15 +95,18 @@ void setupModules()
storeForwardModule = new StoreForwardModule();
#endif
#if defined(ARCH_ESP32) || defined(ARCH_NRF52)
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)
externalNotificationModule = new ExternalNotificationModule();
new RangeTestModule();
#endif
} else {
adminModule = new AdminModule();
#if HAS_TELEMETRY
new DeviceTelemetryModule();
#endif
traceRouteModule = new TraceRouteModule();
}
// NOTE! This module must be added LAST because it likes to check for replies from other modules and avoid sending extra
// acks
routingModule = new RoutingModule();
}
}

View File

@@ -0,0 +1,310 @@
#include "NeighborInfoModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#define MAX_NUM_NEIGHBORS 10 // also defined in NeighborInfo protobuf options
NeighborInfoModule *neighborInfoModule;
static const char *neighborInfoConfigFile = "/prefs/neighbors.proto";
/*
Prints a single neighbor info packet and associated neighbors
Uses LOG_DEBUG, which equates to Console.log
NOTE: For debugging only
*/
void NeighborInfoModule::printNeighborInfo(const char *header, const meshtastic_NeighborInfo *np)
{
LOG_DEBUG("%s NEIGHBORINFO PACKET from Node 0x%x to Node 0x%x (last sent by 0x%x)\n", header, np->node_id,
nodeDB.getNodeNum(), np->last_sent_by_id);
LOG_DEBUG("----------------\n");
LOG_DEBUG("Packet contains %d neighbors\n", np->neighbors_count);
for (int i = 0; i < np->neighbors_count; i++) {
LOG_DEBUG("Neighbor %d: node_id=0x%x, snr=%.2f\n", i, np->neighbors[i].node_id, np->neighbors[i].snr);
}
LOG_DEBUG("----------------\n");
}
/*
Prints the nodeDB nodes so we can see whose nodeInfo we have
NOTE: for debugging only
*/
void NeighborInfoModule::printNodeDBNodes(const char *header)
{
int num_nodes = nodeDB.getNumMeshNodes();
LOG_DEBUG("%s NODEDB SELECTION from Node 0x%x:\n", header, nodeDB.getNodeNum());
LOG_DEBUG("----------------\n");
LOG_DEBUG("DB contains %d nodes\n", num_nodes);
for (int i = 0; i < num_nodes; i++) {
const meshtastic_NodeInfoLite *dbEntry = nodeDB.getMeshNodeByIndex(i);
LOG_DEBUG(" Node %d: node_id=0x%x, snr=%.2f\n", i, dbEntry->num, dbEntry->snr);
}
LOG_DEBUG("----------------\n");
}
/*
Prints the nodeDB neighbors
NOTE: for debugging only
*/
void NeighborInfoModule::printNodeDBNeighbors(const char *header)
{
int num_neighbors = getNumNeighbors();
LOG_DEBUG("%s NODEDB SELECTION from Node 0x%x:\n", header, nodeDB.getNodeNum());
LOG_DEBUG("----------------\n");
LOG_DEBUG("DB contains %d neighbors\n", num_neighbors);
for (int i = 0; i < num_neighbors; i++) {
const meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
LOG_DEBUG(" Node %d: node_id=0x%x, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
}
LOG_DEBUG("----------------\n");
}
/*
Prints the nodeDB with selectors for the neighbors we've chosen to send (inefficiently)
Uses LOG_DEBUG, which equates to Console.log
NOTE: For debugging only
*/
void NeighborInfoModule::printNodeDBSelection(const char *header, const meshtastic_NeighborInfo *np)
{
int num_neighbors = getNumNeighbors();
LOG_DEBUG("%s NODEDB SELECTION from Node 0x%x:\n", header, nodeDB.getNodeNum());
LOG_DEBUG("----------------\n");
LOG_DEBUG("Selected %d neighbors of %d DB neighbors\n", np->neighbors_count, num_neighbors);
for (int i = 0; i < num_neighbors; i++) {
meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
bool chosen = false;
for (int j = 0; j < np->neighbors_count; j++) {
if (np->neighbors[j].node_id == dbEntry->node_id) {
chosen = true;
}
}
if (!chosen) {
LOG_DEBUG(" Node %d: neighbor=0x%x, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
} else {
LOG_DEBUG("---> Node %d: neighbor=0x%x, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
}
}
LOG_DEBUG("----------------\n");
}
/* Send our initial owner announcement 35 seconds after we start (to give network time to setup) */
NeighborInfoModule::NeighborInfoModule()
: ProtobufModule("neighborinfo", meshtastic_PortNum_NEIGHBORINFO_APP, &meshtastic_NeighborInfo_msg),
concurrency::OSThread("NeighborInfoModule"), neighbors(neighborState.neighbors),
numNeighbors(&neighborState.neighbors_count)
{
ourPortNum = meshtastic_PortNum_NEIGHBORINFO_APP;
if (moduleConfig.neighbor_info.enabled) {
this->loadProtoForModule();
setIntervalFromNow(35 * 1000);
} else {
LOG_DEBUG("NeighborInfoModule is disabled\n");
neighborState = meshtastic_NeighborInfo_init_zero;
disable();
}
}
/*
Collect neighbor info from the nodeDB's history, capping at a maximum number of entries and max time
Assumes that the neighborInfo packet has been allocated
@returns the number of entries collected
*/
uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo)
{
uint my_node_id = nodeDB.getNodeNum();
neighborInfo->node_id = my_node_id;
neighborInfo->last_sent_by_id = my_node_id;
neighborInfo->node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval;
int num_neighbors = cleanUpNeighbors();
for (int i = 0; i < num_neighbors; i++) {
meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
if ((neighborInfo->neighbors_count < MAX_NUM_NEIGHBORS) && (dbEntry->node_id != my_node_id)) {
neighborInfo->neighbors[neighborInfo->neighbors_count].node_id = dbEntry->node_id;
neighborInfo->neighbors[neighborInfo->neighbors_count].snr = dbEntry->snr;
// Note: we don't set the last_rx_time and node_broadcast_intervals_secs here, because we don't want to send this over
// the mesh
neighborInfo->neighbors_count++;
}
}
printNodeDBNodes("DBSTATE");
printNodeDBNeighbors("NEIGHBORS");
printNodeDBSelection("COLLECTED", neighborInfo);
return neighborInfo->neighbors_count;
}
/*
Remove neighbors from the database that we haven't heard from in a while
@returns new number of neighbors
*/
size_t NeighborInfoModule::cleanUpNeighbors()
{
uint32_t now = getTime();
int num_neighbors = getNumNeighbors();
NodeNum my_node_id = nodeDB.getNodeNum();
// Find neighbors to remove
std::vector<int> indices_to_remove;
for (int i = 0; i < num_neighbors; i++) {
meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
// We will remove a neighbor if we haven't heard from them in twice the broadcast interval
if ((now - dbEntry->last_rx_time > dbEntry->node_broadcast_interval_secs * 2) && (dbEntry->node_id != my_node_id)) {
indices_to_remove.push_back(i);
}
}
// Update the neighbor list
for (uint i = 0; i < indices_to_remove.size(); i++) {
int index = indices_to_remove[i];
LOG_DEBUG("Removing neighbor with node ID 0x%x\n", neighbors[index].node_id);
for (int j = index; j < num_neighbors - 1; j++) {
neighbors[j] = neighbors[j + 1];
}
(*numNeighbors)--;
}
// Save the neighbor list if we removed any neighbors
if (indices_to_remove.size() > 0) {
saveProtoForModule();
}
return *numNeighbors;
}
/* Send neighbor info to the mesh */
void NeighborInfoModule::sendNeighborInfo(NodeNum dest, bool wantReplies)
{
meshtastic_NeighborInfo neighborInfo = meshtastic_NeighborInfo_init_zero;
collectNeighborInfo(&neighborInfo);
meshtastic_MeshPacket *p = allocDataProtobuf(neighborInfo);
// send regardless of whether or not we have neighbors in our DB,
// because we want to get neighbors for the next cycle
p->to = dest;
p->decoded.want_response = wantReplies;
printNeighborInfo("SENDING", &neighborInfo);
service.sendToMesh(p, RX_SRC_LOCAL, true);
}
/*
Encompasses the full construction and sending packet to mesh
Will be used for broadcast.
*/
int32_t NeighborInfoModule::runOnce()
{
bool requestReplies = false;
sendNeighborInfo(NODENUM_BROADCAST, requestReplies);
return getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_broadcast_interval_secs);
}
/*
Collect a recieved neighbor info packet from another node
Pass it to an upper client; do not persist this data on the mesh
*/
bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *np)
{
printNeighborInfo("RECEIVED", np);
updateNeighbors(mp, np);
// Allow others to handle this packet
return false;
}
/*
Copy the content of a current NeighborInfo packet into a new one and update the last_sent_by_id to our NodeNum
*/
void NeighborInfoModule::updateLastSentById(meshtastic_MeshPacket *p)
{
auto &incoming = p->decoded;
meshtastic_NeighborInfo scratch;
meshtastic_NeighborInfo *updated = NULL;
memset(&scratch, 0, sizeof(scratch));
pb_decode_from_bytes(incoming.payload.bytes, incoming.payload.size, &meshtastic_NeighborInfo_msg, &scratch);
updated = &scratch;
updated->last_sent_by_id = nodeDB.getNodeNum();
// Set updated last_sent_by_id to the payload of the to be flooded packet
p->decoded.payload.size =
pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_NeighborInfo_msg, updated);
}
void NeighborInfoModule::resetNeighbors()
{
*numNeighbors = 0;
neighborState.neighbors_count = 0;
memset(neighborState.neighbors, 0, sizeof(neighborState.neighbors));
saveProtoForModule();
}
void NeighborInfoModule::updateNeighbors(const meshtastic_MeshPacket &mp, const meshtastic_NeighborInfo *np)
{
// The last sent ID will be 0 if the packet is from the phone, which we don't count as
// an edge. So we assume that if it's zero, then this packet is from our node.
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
getOrCreateNeighbor(mp.from, np->last_sent_by_id, np->node_broadcast_interval_secs, mp.rx_snr);
}
}
meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSender, NodeNum n,
uint32_t node_broadcast_interval_secs, float snr)
{
// our node and the phone are the same node (not neighbors)
if (n == 0) {
n = nodeDB.getNodeNum();
}
// look for one in the existing list
for (int i = 0; i < (*numNeighbors); i++) {
meshtastic_Neighbor *nbr = &neighbors[i];
if (nbr->node_id == n) {
// if found, update it
nbr->snr = snr;
nbr->last_rx_time = getTime();
// Only if this is the original sender, the broadcast interval corresponds to it
if (originalSender == n)
nbr->node_broadcast_interval_secs = node_broadcast_interval_secs;
saveProtoForModule(); // Save the updated neighbor
return nbr;
}
}
// otherwise, allocate one and assign data to it
// TODO: max memory for the database should take neighbors into account, but currently doesn't
if (*numNeighbors < MAX_NUM_NEIGHBORS) {
(*numNeighbors)++;
}
meshtastic_Neighbor *new_nbr = &neighbors[((*numNeighbors) - 1)];
new_nbr->node_id = n;
new_nbr->snr = snr;
new_nbr->last_rx_time = getTime();
// Only if this is the original sender, the broadcast interval corresponds to it
if (originalSender == n)
new_nbr->node_broadcast_interval_secs = node_broadcast_interval_secs;
saveProtoForModule(); // Save the new neighbor
return new_nbr;
}
void NeighborInfoModule::loadProtoForModule()
{
if (!nodeDB.loadProto(neighborInfoConfigFile, meshtastic_NeighborInfo_size, sizeof(meshtastic_NeighborInfo),
&meshtastic_NeighborInfo_msg, &neighborState)) {
neighborState = meshtastic_NeighborInfo_init_zero;
}
}
/**
* @brief Save the module config to file.
*
* @return true On success.
* @return false On error.
*/
bool NeighborInfoModule::saveProtoForModule()
{
bool okay = true;
#ifdef FS
FS.mkdir("/prefs");
#endif
okay &= nodeDB.saveProto(neighborInfoConfigFile, meshtastic_NeighborInfo_size, &meshtastic_NeighborInfo_msg, &neighborState);
return okay;
}

View File

@@ -0,0 +1,84 @@
#pragma once
#include "ProtobufModule.h"
/*
* Neighborinfo module for sending info on each node's 0-hop neighbors to the mesh
*/
class NeighborInfoModule : public ProtobufModule<meshtastic_NeighborInfo>, private concurrency::OSThread
{
meshtastic_Neighbor *neighbors;
pb_size_t *numNeighbors;
public:
/*
* Expose the constructor
*/
NeighborInfoModule();
/* Reset neighbor info after clearing nodeDB*/
void resetNeighbors();
bool saveProtoForModule();
// Let FloodingRouter call updateLastSentById upon rebroadcasting a NeighborInfo packet
friend class FloodingRouter;
protected:
// Note: this holds our local info.
meshtastic_NeighborInfo neighborState;
/*
* Called to handle a particular incoming message
* @return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *nb) override;
/*
* Collect neighbor info from the nodeDB's history, capping at a maximum number of entries and max time
* @return the number of entries collected
*/
uint32_t collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo);
/*
Remove neighbors from the database that we haven't heard from in a while
@returns new number of neighbors
*/
size_t cleanUpNeighbors();
/* Allocate a new NeighborInfo packet */
meshtastic_NeighborInfo *allocateNeighborInfoPacket();
// Find a neighbor in our DB, create an empty neighbor if missing
meshtastic_Neighbor *getOrCreateNeighbor(NodeNum originalSender, NodeNum n, uint32_t node_broadcast_interval_secs, float snr);
/*
* Send info on our node's neighbors into the mesh
*/
void sendNeighborInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
size_t getNumNeighbors() { return *numNeighbors; }
meshtastic_Neighbor *getNeighborByIndex(size_t x)
{
assert(x < *numNeighbors);
return &neighbors[x];
}
/* update neighbors with subpacket sniffed from network */
void updateNeighbors(const meshtastic_MeshPacket &mp, const meshtastic_NeighborInfo *np);
/* update a NeighborInfo packet with our NodeNum as last_sent_by_id */
void updateLastSentById(meshtastic_MeshPacket *p);
void loadProtoForModule();
/* Does our periodic broadcast */
int32_t runOnce() override;
/* These are for debugging only */
void printNeighborInfo(const char *header, const meshtastic_NeighborInfo *np);
void printNodeDBNodes(const char *header);
void printNodeDBNeighbors(const char *header);
void printNodeDBSelection(const char *header, const meshtastic_NeighborInfo *np);
};
extern NeighborInfoModule *neighborInfoModule;

View File

@@ -11,8 +11,8 @@
PositionModule *positionModule;
PositionModule::PositionModule()
: ProtobufModule("position", meshtastic_PortNum_POSITION_APP, &meshtastic_Position_msg), concurrency::OSThread(
"PositionModule")
: ProtobufModule("position", meshtastic_PortNum_POSITION_APP, &meshtastic_Position_msg),
concurrency::OSThread("PositionModule")
{
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
setIntervalFromNow(60 * 1000); // Send our initial position 60 seconds after we start (to give GPS time to setup)
@@ -52,11 +52,22 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
nodeDB.updatePosition(getFrom(&mp), p);
// Only respond to location requests on the channel where we broadcast location.
if (channels.getByIndex(mp.channel).role == meshtastic_Channel_Role_PRIMARY) {
ignoreRequest = false;
} else {
ignoreRequest = true;
}
return false; // Let others look at this message also if they want
}
meshtastic_MeshPacket *PositionModule::allocReply()
{
if (ignoreRequest) {
return nullptr;
}
meshtastic_NodeInfoLite *node = service.refreshLocalMeshNode(); // should guarantee there is now a position
assert(node->has_position);
@@ -131,6 +142,11 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha
service.cancelSending(prevPacketId);
meshtastic_MeshPacket *p = allocReply();
if (p == nullptr) {
LOG_WARN("allocReply returned a nullptr");
return;
}
p->to = dest;
p->decoded.want_response = wantReplies;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER)
@@ -174,7 +190,7 @@ int32_t PositionModule::runOnce()
} else if (config.position.position_broadcast_smart_enabled) {
// Only send packets if the channel is less than 25% utilized or we're a tracker.
if (airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER)) {
meshtastic_NodeInfoLite *node2 = service.refreshLocalMeshNode(); // should guarantee there is now a position
const meshtastic_NodeInfoLite *node2 = service.refreshLocalMeshNode(); // should guarantee there is now a position
if (hasValidPosition(node2)) {
// The minimum distance to travel before we are able to send a new position packet.

View File

@@ -1,3 +1,13 @@
/**
* @file RangeTestModule.cpp
* @brief Implementation of the RangeTestModule class and RangeTestModuleRadio class.
*
* As a sender, this module sends packets every n seconds with an incremented PacketID.
* As a receiver, this module receives packets from multiple senders and saves them to the Filesystem.
*
* The RangeTestModule class is an OSThread that runs the module.
* The RangeTestModuleRadio class handles sending and receiving packets.
*/
#include "RangeTestModule.h"
#include "FSCommon.h"
#include "MeshService.h"
@@ -10,11 +20,6 @@
#include "gps/GeoCoord.h"
#include <Arduino.h>
/*
As a sender, I can send packets every n seconds. These packets include an incremented PacketID.
As a receiver, I can receive packets from multiple senders. These packets can be saved to the Filesystem.
*/
RangeTestModule *rangeTestModule;
RangeTestModuleRadio *rangeTestModuleRadio;
@@ -97,6 +102,12 @@ int32_t RangeTestModule::runOnce()
return disable();
}
/**
* Sends a payload to a specified destination node.
*
* @param dest The destination node number.
* @param wantReplies Whether or not to request replies from the destination node.
*/
void RangeTestModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
{
meshtastic_MeshPacket *p = allocDataPacket();

View File

@@ -29,7 +29,7 @@ class RangeTestModuleRadio : public SinglePortModule
uint32_t lastRxID = 0;
public:
RangeTestModuleRadio() : SinglePortModule("RangeTestModuleRadio", meshtastic_PortNum_TEXT_MESSAGE_APP)
RangeTestModuleRadio() : SinglePortModule("RangeTestModuleRadio", meshtastic_PortNum_RANGE_TEST_APP)
{
loopbackOk = true; // Allow locally generated messages to loop back to the client
}

View File

@@ -86,7 +86,13 @@ SerialModuleRadio::SerialModuleRadio() : MeshModule("SerialModuleRadio")
}
}
// For the serial2 port we can't really detect if any client is on the other side, so instead just look for recent messages
/**
* @brief Checks if the serial connection is established.
*
* @return true if the serial connection is established, false otherwise.
*
* For the serial2 port we can't really detect if any client is on the other side, so instead just look for recent messages
*/
bool SerialModule::checkIsConnected()
{
uint32_t now = millis();
@@ -191,6 +197,11 @@ int32_t SerialModule::runOnce()
}
}
/**
* Allocates a new mesh packet for use as a reply to a received packet.
*
* @return A pointer to the newly allocated mesh packet.
*/
meshtastic_MeshPacket *SerialModuleRadio::allocReply()
{
auto reply = allocDataPacket(); // Allocate a packet for sending
@@ -198,9 +209,15 @@ meshtastic_MeshPacket *SerialModuleRadio::allocReply()
return reply;
}
/**
* Sends a payload to a specified destination node.
*
* @param dest The destination node number.
* @param wantReplies Whether or not to request replies from the destination node.
*/
void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
{
meshtastic_Channel *ch = (boundChannel != NULL) ? &channels.getByName(boundChannel) : NULL;
const meshtastic_Channel *ch = (boundChannel != NULL) ? &channels.getByName(boundChannel) : NULL;
meshtastic_MeshPacket *p = allocReply();
p->to = dest;
if (ch != NULL) {
@@ -216,6 +233,12 @@ void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
service.sendToMesh(p);
}
/**
* Handle a received mesh packet.
*
* @param mp The received mesh packet.
* @return The processed message.
*/
ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp)
{
if (moduleConfig.serial.enabled) {
@@ -249,7 +272,7 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp
if (moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_DEFAULT ||
moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_SIMPLE) {
serialPrint->printf("%s", p.payload.bytes);
serialPrint->write(p.payload.bytes, p.payload.size);
} else if (moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG) {
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(getFrom(&mp));
String sender = (node && node->has_user) ? node->user.short_name : "???";
@@ -277,6 +300,11 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
/**
* @brief Returns the baud rate of the serial module from the module configuration.
*
* @return uint32_t The baud rate of the serial module.
*/
uint32_t SerialModule::getBaudRate()
{
if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_110) {

View File

@@ -17,7 +17,8 @@ int32_t DeviceTelemetryModule::runOnce()
uint32_t now = millis();
if (((lastSentToMesh == 0) ||
((now - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval))) &&
airTime->isTxAllowedChannelUtil() && airTime->isTxAllowedAirUtil()) {
airTime->isTxAllowedChannelUtil() && airTime->isTxAllowedAirUtil() &&
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
sendTelemetry();
lastSentToMesh = now;
} else if (service.isToPhoneQueueEmpty()) {
@@ -30,6 +31,10 @@ int32_t DeviceTelemetryModule::runOnce()
bool DeviceTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *t)
{
// Don't worry about storing telemetry in NodeDB if we're a repeater
if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER)
return false;
if (t->which_variant == meshtastic_Telemetry_device_metrics_tag) {
#ifdef DEBUG_PORT
const char *sender = getSenderShortName(mp);
@@ -43,7 +48,19 @@ bool DeviceTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
return false; // Let others look at this message also if they want
}
bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
meshtastic_MeshPacket *DeviceTelemetryModule::allocReply()
{
if (ignoreRequest) {
return NULL;
}
LOG_INFO("Device telemetry replying to request\n");
meshtastic_Telemetry telemetry = getDeviceTelemetry();
return allocDataProtobuf(telemetry);
}
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
{
meshtastic_Telemetry t;
@@ -60,16 +77,22 @@ bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
t.variant.device_metrics.channel_utilization = airTime->channelUtilizationPercent();
t.variant.device_metrics.voltage = powerStatus->getBatteryVoltageMv() / 1000.0;
LOG_INFO("(Sending): air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f\n",
t.variant.device_metrics.air_util_tx, t.variant.device_metrics.channel_utilization,
t.variant.device_metrics.battery_level, t.variant.device_metrics.voltage);
return t;
}
meshtastic_MeshPacket *p = allocDataProtobuf(t);
bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry telemetry = getDeviceTelemetry();
LOG_INFO("(Sending): air_util_tx=%f, channel_utilization=%f, battery_level=%i, voltage=%f\n",
telemetry.variant.device_metrics.air_util_tx, telemetry.variant.device_metrics.channel_utilization,
telemetry.variant.device_metrics.battery_level, telemetry.variant.device_metrics.voltage);
meshtastic_MeshPacket *p = allocDataProtobuf(telemetry);
p->to = dest;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_MIN;
nodeDB.updateTelemetry(nodeDB.getNodeNum(), t, RX_SRC_LOCAL);
nodeDB.updateTelemetry(nodeDB.getNodeNum(), telemetry, RX_SRC_LOCAL);
if (phoneOnly) {
LOG_INFO("Sending packet to phone\n");
service.sendToPhone(p);

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