Compare commits

...

104 Commits

Author SHA1 Message Date
github-actions[bot]
98cb3edff1 [create-pull-request] automated change (#4506)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-08-19 17:21:03 -05:00
Ben Meadors
91d661246a Milliseconds! 2024-08-19 06:55:58 -05:00
todd-herbert
a8999d7759 Don't manually clear runAsap flag (#4496) 2024-08-18 07:22:16 -05:00
Jonathan Bennett
1bbc273ba6 Don't reject network time updates unintentionally (#4489) 2024-08-17 17:35:05 -05:00
Mictronics
e37acae405 Fix RF switching logic on rp2040-lora board. (#4486)
* Fix LED pinout for T-Echo board marked v1.0, date 2021-6-28

* Merge PR #420

* Fixed double and missing Default class.

* Use correct format specifier and fixed typo.

* Removed duplicate code.

* Fix error: #if with no expression

* Fix warning: extra tokens at end of #endif directive.

* Fix antenna switching logic. Complementary-pin control logic is required on the rp2040-lora board.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-17 15:09:13 -05:00
Ben Meadors
5ff1078c8c Move NeighborInfo interval default to 6 hours (double NodeInfo) (#4483)
* Move NeighborInfo in line with NodeInfo

* Set default to 6 hours and cap minimum at 2 hours
2024-08-17 11:51:53 -05:00
Matt Ranostaj
a577dd4142 define PERIPHERAL_WARMUP_MS for heltec_capsule_sensor_v3 (#4473)
* delay is at least needed for the GXHTC3 module to be detected
2024-08-17 06:37:05 -05:00
todd-herbert
e0b4a8e31e Radio Master Joystick (#4476)
* Radio Master Bandit 5-Way Joystick: first draft
Untested on genuine hardware

* "Okay" moves to next frame, even when canned message disabled

* Refactor to allow easier customization

* Implement feedback from testing
* guard toggleGPS()
* show "Shutting down.." screen
* split adhoc ping alert onto two lines

* Don't block while waiting for shutdown
Was preventing the alert from showing
2024-08-17 05:51:53 -05:00
Mark Trevor Birss
0ebdc7ab0c Update architecture.h add Minewsemi ME25LS01 LR1110 breakout ME25LE01_V1.0 and fix buzzer (#4472)
* Update architecture.h

* Update variant.h

* Update variant.h

* Update architecture.h

* Update architecture.h

* Delete src/platform/nrf52/architecture.h

* Add files via upload

* Update architecture.h

* Update architecture.h

* Update architecture.h
2024-08-16 17:37:22 -05:00
Christopher Hoover
85176756ec Adds ASCII log option needed by portudino (#4443)
* Adds ASCII logs useful to portudino

Activates ASCII log option when stdout is not a terminal.  This is
generally the right thing to do; if not, the behavior can be
overridden in config.yaml using AsciiLogs under Logging.  The result
is reasonable system logs for portudino when running under systemd or
the like.

Signed-off-by: Christopher Hoover <ch@murgatroid.com>
2024-08-15 19:09:06 -05:00
Ben Meadors
d398419aef Router and sensor are impolite (#4468) 2024-08-15 08:47:49 -05:00
Ben Meadors
efc27f2051 Initial telemetry with time and variant tags (#4463) 2024-08-14 16:24:28 -05:00
github-actions[bot]
837c4e9e7b [create-pull-request] automated change (#4461)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-08-14 09:33:57 -05:00
Christopher Hoover
181325103a Improves ignore messages in Router.cpp (#4442)
Signed-off-by: Christopher Hoover <ch@murgatroid.com>

.
2024-08-14 07:51:32 -05:00
Ben Meadors
207b9b49a5 Always attempt to set NTP or GPS time on a fresh position packet (#4460) 2024-08-14 07:42:30 -05:00
Ben Meadors
464f270b12 More explicit guards for attempting to set RTC (#4452)
* Guard against timesources from the mesh if we have good time

* Trunk

* Consider phone time in the past 24 hours authoritative as well

* Rename

* GPS can be null

* Declaration

* Remove RemoteHardware

* Explicitly remove GPS

* Exclude GPS earlier for RAK2560
2024-08-13 06:56:20 -05:00
Ben Meadors
7740b4bccd Sweep up some missed trunk formatting 2024-08-13 06:52:03 -05:00
Ben Meadors
e85a2e827b Update protos 2024-08-13 06:49:32 -05:00
geeksville
62a0321c7d Fixes for #4395: nrf52 flash filesystem reliability (#4406)
* bug #4184: fix config file loss due to filesystem write errors
* Use SafeFile for atomic file writing (with xor checksum readback)
* Write db.proto last because it could be the largest file on the FS (and less critical)
* Don't keep a tmp file around while writing db.proto (because too big to fit two files in the filesystem)
* generate a new critial fault if we encounter errors writing to flash
either CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE or CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE
(depending on if the second write attempt worked)
* reformat the filesystem if we detect it is corrupted (then rewrite our config files) (only on nrf52 - not sure
yet if we should bother on ESP32)
* If we have to format the FS, make sure to preserve the oem.proto if it exists

* add logLegacy() so old C code in libs can log via our logging

* move filesList() to a better location (used only in developer builds)

* Reformat with "trunk fmt" to match our coding conventions

* for #4395: don't use .exists() to before attempting file open
If a LFS filesystem is corrupted, .exists() can fail when a mere .open()
attempt would have succeeded.  Therefore better to do the .open() in hopes that
we can read the file (in case we need to reformat to fix the FS).
(Seen and confirmed in stress testing)

* for #4395 more fixes, see below for details:
* check for LFS assertion failures during file operations (needs customized lfs_util.h to provide suitable hooks)
* Remove fsCheck() because checking filesystem by writing to it is very high risk, it makes likelyhood that we will
be able to read the config protobufs quite low.
* Update the LFS inside of adafruitnrf52 to 1.7.2 (from their old 1.6.1) to get the following fix:
97d8d5e96a

* use disable_adafruit_usb.py now that we are (temporarily?) using a forked adafruit lib
We need to reach inside the adafruit project and turn off USE_TINYUSB, just doing that
from platformio.ini is no longer sufficient.

Tested on a wio-sdk-wm1110 board (which is the only board that had this problem)

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-13 06:45:39 -05:00
Aaron.Lee
6e8300287b Heltec boards sensor and low power features update (#4418)
* Update sensor drive and low power features.

* Update ST7789 TFT control logic.

* Update Heltec nRF board low power features.

* Update the GPS UART port pointer
2024-08-13 06:30:35 -05:00
GUVWAF
f97ae52263 STM32WL improvements (#4449)
* STM32WL: Enable DeviceTelemetry

* Add long/short name user preference options

* Add new STM32WL-based hardware models
2024-08-12 20:31:45 -05:00
Ben Loomis
c74bce9360 Detect UM600 as UC6580 (#4444) 2024-08-12 06:40:57 -05:00
github-actions[bot]
6cd1882aaa [create-pull-request] automated change (#4439)
Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com>
2024-08-11 17:22:01 -05:00
Tom Fifield
cf392a4c20 Address some FIXME comments (#4435)
* Address some FIXME comments

These comments have since been addressed by more modern code.
Remove them to reduce the clutter in the codebase.

* Remove 'dumb idea' from SimpleAllocator

4 year old code that was set never to run can probably be safely
deleted.
2024-08-11 07:06:38 -05:00
Mictronics
8daebf80dd Fix warning: extra tokens at end of #endif directive. (#4432) 2024-08-10 12:32:52 -05:00
GUVWAF
debf4b934f Fix for "has default channel" with empty channel name (#4430) 2024-08-09 15:26:22 -05:00
Ben Meadors
3878e025e4 Split factory reset into config and device variants (#4427)
* Split factory reset into config and device variants

* Trunk

* Default only in header
2024-08-09 08:38:29 -05:00
github-actions[bot]
3ab4bebdcb [create-pull-request] automated change (#4426)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-08-09 06:37:49 -05:00
Tom Fifield
e38aca3cba NimbleBluetooth.h is not required in MeshService. (#4419)
There are no calls to the functions defined in Nimble from this
class. See also older comment on line 8 about the dream to seperate
mesh and bluetooth :)
2024-08-09 06:35:42 -05:00
github-actions[bot]
d8bdb92efe [create-pull-request] automated change (#4409)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-08-09 06:35:26 -05:00
Jonathan Bennett
c6a9edf8c7 Move printBytes to meshUtils (#4424) 2024-08-09 01:43:13 -05:00
Jonathan Bennett
a7da3537e2 Adds MESHTASTIC_EXCLUDE_TZ option (#4423) 2024-08-09 00:52:31 -05:00
Ben Meadors
5b4530325f Short circuit while the probe code does not auto-detect 2024-08-08 20:53:26 -05:00
Tom Fifield
b498c0bfbf [WIP] Add support for Airoha AG3335 GPS (#4394)
* Add GPS detection code for Airoha AG3335

Airoha AG3335 is used in Seeed T-1000E Tracker

* Add support for Airoha AG3335

Airoha AG3335 is used in Seeed T-1000E Tracker. This adds
detection code, and code to configure its use.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-08 20:18:18 -05:00
Tom Fifield
02ae24b6fa Remove outdated comments (#4417)
These comments from four years ago no longer reflect how things
work.
2024-08-08 20:08:14 -05:00
Tilen Komel
5111bd703a Updted protobuf url (#4411) 2024-08-07 07:23:31 -05:00
Jonathan Bennett
789e8f02bf Add more exclude options to save program ram/flash (#4408)
* Add PowerFSM Exclude option

* Add TEXTMESSAGE module exclude option

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-06 18:48:55 -05:00
todd-herbert
92526fca23 "Scan and Select" input for Canned Messages (#4365)
* Add "Scan and Select" input method for canned messages

* Adapt canned message drawing if USE_EINK
* Indicate current selection with indent rather than inverse text
* Avoid large text on "sending" and delivery report pop-ups
* Fit SNR and RSSI details on screen

* Change hash function which detects changes in E-Ink images
The old function struggled to distingush between images on the canned-message frame, failing to update when scrolling between messages. No real justification for the new algorithm, other than "it works" and doesn't seem "too expensive". For context, this function runs once a second.

* Use canned messages (scan and select) by default with HT-VME213 and HT-VME290

* Guard for HAS_SCREEN
2024-08-06 17:16:56 -05:00
HarukiToreda
9ec7dbd695 Initial Support for Heltec VM-T190 (#4391)
Initial Support for Heltec VM-T190
2024-08-06 13:59:33 -05:00
geeksville
66c41e683d bug #4184: fix config file loss due to filesystem write errors (#4397)
* Use SafeFile for atomic file writing (with xor checksum readback)
* Write db.proto last because it could be the largest file on the FS (and less critical)
* Don't keep a tmp file around while writing db.proto (because too big to fit two files in the filesystem)
* generate a new critial fault if we encounter errors writing to flash
either CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE or CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE
(depending on if the second write attempt worked)
* reformat the filesystem if we detect it is corrupted (then rewrite our config files) (only on nrf52 - not sure
yet if we should bother on ESP32)
* If we have to format the FS, make sure to preserve the oem.proto if it exists

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-06 13:59:06 -05:00
geeksville
c1870f91fc Finish powermon/powerstress (#4230)
* Turn off vscode cmake prompt - we don't use cmake on meshtastic

* Add rak4631_dap variant for debugging with NanoDAP debug probe device.

* The rak device can also run freertos (which is underneath nrf52 arduino)

* Add semihosting support for nrf52840 devices
Initial platformio.ini file only supports rak4630
Default to non TCP for the semihosting log output for now...
Fixes https://github.com/meshtastic/firmware/issues/4135

* powermon WIP (for https://github.com/meshtastic/firmware/issues/4136 )

* oops - mean't to mark the _dbg variant as an 'extra' board.

* powermon wip

* Make serial port on wio-sdk-wm1110 board work
By disabling the (inaccessible) adafruit USB

* Instrument (radiolib only for now) lora for powermon
per https://github.com/meshtastic/firmware/issues/4136

* powermon gps support
https://github.com/meshtastic/firmware/issues/4136

* Add CPU deep and light sleep powermon states
https://github.com/meshtastic/firmware/issues/4136

* Change the board/swversion bootstring so it is a new "structured" log msg.

* powermon wip

* add example script for getting esp S3 debugging working
Not yet used but I didn't want these nasty tricks to get lost yet.

* Add PowerMon reporting for screen and bluetooth pwr.

* make power.powermon_enables config setting work.

* update to latest protobufs

* fix bogus shellcheck warning

* make powermon optional (but default enabled because tiny and no runtime impact)

* tell vscode, if formatting, use whatever our trunk formatter wants
without this flag if the user has set some other formatter (clang)
in their user level settings, it will be looking in the wrong directory
for the clang options (we want the options in .trunk/clang)

Note: formatOnSave is true in master, which means a bunch of our older
files are non compliant and if you edit them it will generate lots of
formatting related diffs.  I guess I'll start letting that happen with
my future commits ;-).

* add PowerStress module

* nrf52 arduino is built upon freertos, so let platformio debug it

* don't accidentally try to Segger ICE if we are using another ICE

* clean up RedirectablePrint::log so it doesn't have three very different implementations inline.

* remove NoopPrint - it is no longer needed

* when talking to API clients via serial, don't turn off log msgs instead encapsuate them

* fix the build - would loop forever if there were no files to send

* don't use Segger code if not talking to a Segger debugger

* when encapsulating logs, make sure the strings always has nul terminators

* nrf52 soft device will watchdog if you use ICE while BT on...
so have debugger disable bluetooth.

* Important to not print debug messages while writing to the toPhone scratch buffer

* don't include newlines if encapsulating log records as protobufs

* update to latest protobufs (needed for powermon goo)

* PowerStress WIP

* for #4154 and #4136 add concept of dependent gpios...
Which is currently only tested with the LED but eventually
will be used for shared GPIO/screen power rail enable
and LED forcing (which is a sanity check in the power stress
testing)

* fix linter warning

* Transformer is a better name for the LED input > operation > output classes

* PMW led changes to work on esp32-s3

* power stress improvements

* allow ble logrecords to be fetched either by NOTIFY or INDICATE ble types

This allows 'lossless' log reading.  If client has requested INDICATE
(rather than NOTIFY) each log record emitted via log() will have to fetched
by the client device before the meshtastic node can continue.

* Fix serious problem with nrf52 BLE logging.
When doing notifies of LogRecords it is important to use the
binary write routines - writing using the 'string' write won't work.
Because protobufs can contain \0 nuls inside of them which if being
parsed as a string will cause only a portion of the protobuf to be sent.
I noticed this because some log messages were not getting through.

* fix gpio transformer stuff to work correctly with LED_INVERTED

Thanks @todd-herbert for noticing this and the great stack trace.
The root cause was that I had accidentially shadowed outPin in a subclass
with an unneeded override.  It would break on any board that had inverted
LED power.

fixes
https://github.com/meshtastic/firmware/pull/4230#pullrequestreview-2217389099

* Support driving multiple output gpios from one input.

While investigating https://github.com/meshtastic/firmware/pull/4230#pullrequestreview-2217389099
I noticed in variant.h that there are now apparently newer TBEAMs than mine
that have _both_ a GPIO based power LED and the PMU based LED.  Add a splitter
so that we can drive two output GPIOs from one logical signal.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-06 12:35:54 -05:00
Tom Fifield
06eaf2ba5d Use sys.executable to refer to python. (#4402)
Thanks to @mrekin for testing the build on Windows.
The previous fix for this UF2 call did not work.
sys.executable should fix it.
2024-08-06 06:48:05 -05:00
github-actions[bot]
4a79a690db [create-pull-request] automated change (#4400)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-08-05 20:14:07 -05:00
Tom Fifield
1f458d6397 Make UF2 build command windows-friendly (#4399)
As reported by @mrekin, the previous changes to the platformio
custom build script may not work on windows. Change to use
python3 instead of a call to /usr/bin/env python3.
2024-08-05 19:25:47 -05:00
geeksville
02231fd487 fix minor type comparison warning that I saw in the build (#4398) 2024-08-05 17:07:43 -05:00
Tom Fifield
66a4632f34 Fix python call in UF2 generation. (#4392)
The call to generate UF2 files in the platformio custom script was
a bare call to python. In some environments, this command won't
exist in this way. Instead, use the standard env approach to find
the right python.

Additionally, add the shebang line on line 1 so this script can be
executed standalone if needed.
2024-08-05 10:00:52 -05:00
Tom Fifield
e509a91019 Fix UC6580 ifdefs (#4393)
meshtastic/firmware#4319 added autodetect code for UC6580, and
removed these ifdefs. However, they were inadvertantly re-added
by #4328 .
2024-08-05 10:00:32 -05:00
geeksville
d8f3c3324c Make lora radio reset reliable on wio-tracker-1100 and lower lr11x0 power consumption in sleep (#4383)
* Fix wio-tracker-1110 lora radio reset GPIO assignment
This fixes flaky lora radio init on this board.

* No need to keep lr11x0 radio config during sleep anymore, also stop TCXO

I think the problem (at least on the board I'm using for power testing
a wio tracker 1110) was that actually the RESET GPIO was not correct for
the radio.  This led to the radio not being properly reinited after exiting
sleep mode.

Now that the GPIO is fixed I can enter deep sleep (fully shutting down
radio) and then later when the CPU resets, it can successfully init the
radio and send packets.

After this seeming success, I also turned off the TCXO during sleep and that
worked as well.
2024-08-05 06:47:04 -05:00
Max
9ddfc6de4c Commented RF95(1276) as no needed right now (#4386) 2024-08-05 06:02:54 -05:00
geeksville
1a38c4e51d Remove LED_INVERTED, see below for why ;-) (#4382)
While working on #4378 I noticed a funny problem: the blinking system
LED was on during deep-sleep.  Initially I thought it was some weird
sleep hw config thing but it turns out it was easier but more pervasive.

We had two different preprocessor symbols which both meant approximately the same
thing LED_INVERTED and LED_STATE_ON (though their polarity was opposite).
Some variant files were setting one, others were setting the other, and others were
setting both. heh.

In the case of the board I was testing (seeed tracker wio 1100) it was only setting one
and the default behavior for the other (for all boards) was incorrect.  So I did a grep
and it seems like LED_STATE_ON was used more often, so I kept that one and removed
LED_INVERTED everywhere.
2024-08-05 06:02:32 -05:00
Mark Trevor Birss
40d6b99911 Add Minewsemi LR1110+nRF52840-ME25LS01 [both 4.2inch e-ink and non e-ink varaint] (#4387)
* Add files via upload

* Update EInkDisplay2.cpp

* Add files via upload

* Update platformio.ini

* Update platformio.ini

* Update platformio.ini

* Update platformio.ini
2024-08-05 05:59:57 -05:00
Jonathan Bennett
7d00e1cef9 Output more useful log message when the NodeDB is full (#4389) 2024-08-04 18:52:10 -05:00
Ken Piper
5bbafdfd31 Configure pin modes of selected pins before attempting to write to them (#4385) 2024-08-04 06:06:36 -05:00
Jonathan Bennett
5453c495e2 Actually set the rand() seed for Portduino (#4380) 2024-08-03 13:12:22 -05:00
geeksville
d1ff160256 Generalize SWD debugging stuff so it works on all nrf52 targets. (#4377)
* add bootloader install script for wio tracker 1110 board
Mostly for documentation purposes for future devs.

* Generalize nrf52 hw debugging support so it works on all nrf52 targets
2024-08-03 07:41:35 -05:00
geeksville
dd552a99e1 fix #4367 make USB power detection work correctly on seeed trackers (#4376)
for wio tracker 1110 and 1000-E and possibly other nrf52 boards.
The problem was that nrf52 power stuff wasn't generating regular
powerstatus notifications (because that code was guarded by a batteryLevel
check which was null for those boards).  So I've cleaned up the battery status stuff
a bit and we now have fewer special cases.
Tested on a 1000-E, tracker 1110 and a rak4631 board.

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-02 20:20:44 -05:00
geeksville
09ea198205 Automatically generate .uf2 files anytime we generate a .hex file for nrf52 (#4370)
* Automatically generate .uf2 files (which are often used by nrf52 bootloaders for installing
app loads) anytime we generate a new hex file.  This tool takes very little time to run and it is handy
for development

* Remove an old custom target I had tried to add to autogen uf2 files (that never worked)

Build output now looks like:

$ pio run --environment tracker-t1000-e
Processing tracker-t1000-e (board: tracker-t1000-e; platform: platformio/nordicnrf52@^10.5.0; framework: arduino)
...
Generating UF2 file
Converting to uf2, output size: 1395200, start address: 0x27000
Wrote 1395200 bytes to /home/kevinh/development/meshtastic/firmware/.pio/build/tracker-t1000-e/firmware.uf2
Building .pio/build/tracker-t1000-e/firmware.zip
Zip created at .pio/build/tracker-t1000-e/firmware.zip
=================================================================================== [SUCCESS] Took 9.33 seconds ===================================================================================

Environment      Status    Duration
---------------  --------  ------------
tracker-t1000-e  SUCCESS   00:00:09.327
=================================================================================== 1 succeeded in 00:00:09.327 ===================================================================================

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-02 18:55:04 -05:00
geeksville
703da1d8c7 Fix build to not use incorrect OneButton version (#4374)
* Fix build to not use incorrect OneButton version
OneButton pushed out a new update today that has a different API
rather than just use whichever new version they push, stay on 2.5.x until
someone sees a need to update.  Fixes build for wm1100 tracker.

* Update stm32.ini

* 2.6.1

* Try github tag instead?

* Update stm32.ini

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-08-02 18:28:04 -05:00
Tom Fifield
48c0635188 Remove OSTimer (#4373)
This code is not included anywhere, and none of the functions are
called by code elsewhere in meshtastic.
2024-08-02 10:10:55 -05:00
Tom Fifield
8db6039264 Remove empty file, StatusHandler.h (#4372)
Was added 4 years ago, never used/modified.
2024-08-02 06:32:28 -05:00
todd-herbert
4b4c1669a9 Initial support for HT-VME290, sleep fixes for HT-VME213 (#4334)
* Fix I2C pindefs

* Initial driver testing for HT-VME290

* E-Ink full refresh after canned message pop up

* Tidy variant folders

* Clean ESP32 cpuDeepSleep method
Merge sections, and remove the random assortment of gpio holds left behind.

* Enable 32kHz in variant.h

* Orient E290 with LoRa antenna facing up

* Revert "Clean ESP32 cpuDeepSleep method"

This reverts commit cb8ee508ec2d6bc27a8e228021fd1efbd034c4a0.

* Reduce deep-sleep current for VME213 (non-intrusive)
Originally I made an attempt at tidying up the cpuDeepSleep method, but have reverted that. New commit makes only the changes needed to support VME213. Don't really want the headache of breaking sleep for other variants, especially when this PR is just about implementing new boards.

* Update lib_deps; remove board_level extra
2024-08-02 14:03:59 +12:00
Jonathan Bennett
d2ea430a3e Make SPI frequency and TOPHONE queue size configurable on Native (#4369)
* Make SPI frequency configurable on Native

* Make the tophone queue size configurable for Portduino

* The modified SPISettings must be configured in setup(), after config.yaml is processed

* make MeshService a pointer, so we can configure MAX_RX_TOPHONE at run time

* Got a little over excited with refactoring

* Silence a warning
2024-08-01 19:29:49 -05:00
Michael Gjelsø
4c1c5b070e Changed a RADIOMASTER_900_BANDIT_NANO to DISPLAY_FLIP_SCREEN (#4366)
* More compatible

Changed a RADIOMASTER_900_BANDIT_NANO to DISPLAY_FLIP_SCREEN that is responsible for flipping the OLED screen for better compatible with other devices.

* Update variant.h Radiomaster 

Remove a un-used SCREEN_ROTATE and added DISPLAY_FLIP_SCREEN
2024-08-01 11:53:38 -05:00
Ben Meadors
bcdda4de8a Missed some includes of userPrefs that would allow behavior we don't want 2024-07-31 08:53:59 -05:00
Ben Meadors
24ecfa1a45 Trunk fmt 2024-07-31 07:42:23 -05:00
Sylvain Migaud
106a50bce2 Adding support for Chatter keypad (#4022)
* Adding support for Chatter keypad

* Remove user button mapping since full keypad is now useable

* Adding TAB key and RIGHT to allow selecting a destination

* Fix shift bug (there's only three levels, not four)

* reformat file

* Fix bug with fast repeated keypresses

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-07-31 07:38:21 -05:00
Tom Fifield
103ab0c242 Cleanup - remove unused defines. (#4353)
* Cleanup - remove unused defines.

There were a number of defined variables that were carried over
from old code. - Removed.
Also a typo. - Fixed fix.
Also duplicate definitions of the number of seconds in a day.
-deduplicated.

* Cleanup - remove unused defines.

There were a number of defined variables that were carried over
from old code. - Removed.
Also a typo. - Fixed fix.
Also duplicate definitions of the number of seconds in a day.
-deduplicated.

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-07-31 06:56:06 -05:00
Alexander Smyslov
848b9773b9 Add trackerd to build. (#4347)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-07-31 05:58:41 -05:00
Tom Fifield
ce1eb149ac Cleanup for Air530z GPS (#4344)
It turns out that the Air530z is a GNSS_MODEL_MTK. Our existing
code detects and configures it properly. This patch removes a
couple of AIROHA ifdefs since they are not required for a
working GPS.

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-07-31 05:58:19 -05:00
Ben Meadors
29fe6e7448 Event mode: Block problematic portnums of traffic (#4362) 2024-07-31 05:52:17 -05:00
Oliver0804
a111f54b61 Add #define USE_SSD1306 to avoid automatic detection causing pixel shift. (#4356)
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-07-30 17:15:50 -05:00
Tom Fifield
9f5f630dca Remove unused define in NRF52 architecture (#4350)
The NRF52 architecture defaulted to setting GPS_UBLOX.
However, GPS_UBLOX is not used anywhere in the code.

Remove these lines.
2024-07-30 15:57:31 -05:00
Tom Fifield
1f9dacf486 Add support for ATGM332D series (#4351)
Adds detection code for the ATGM332D series of chips (11, 21, 31,
51, 71), and sets meshtastic to use GNSS_MODEL_ATGM336H for them.
2024-07-30 15:57:13 -05:00
Ben Meadors
5dde738a31 Trunk 2024-07-30 15:08:38 -05:00
Michael Gjelsø
1951569b1a PA FAN Disable (#4355)
* PA FAN Disable

* PA FAN Disable

* Trunk

* Trunk

* Trunk

* Thunk .....
2024-07-30 15:05:51 -05:00
Ben Meadors
93ba19d1e1 Make LogRecord protobuf serial logging over Phone API opt-in instead (#4358)
* Make LogRecord protobuf serial logging over Phone API opt-in instead of enabled by default

* debug_log_enabled
2024-07-30 15:05:33 -05:00
github-actions[bot]
a1c998e7e0 [create-pull-request] automated change (#4361)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-07-30 14:57:24 -05:00
github-actions[bot]
302caa854a [create-pull-request] automated change (#4354)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-07-30 09:13:25 -05:00
Ben Meadors
59cc57fc29 Event mode: Enforce reliable hop limit and disallow default public MQTT (#4343)
* Event mode: Enforce reliable hop limit

* Event mode: Short circuit wantsLink on MQTT for default broker address

* Just enforce at channels level since everything uses this

* For events never forward packets with excessive hop_limit

* In EVENT_MODE, don't respond with hop_limit set more then the configured max.

* Correct hop_start when correcting hop_limit in event mode.

* Make EVENT_MODE work from userPrefs.h

* Event mode: Disallow Router or Repeater roles

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2024-07-29 20:16:47 -05:00
Ben Meadors
6813b8e4e9 Just literally trying stuff at this point 2024-07-29 06:52:14 -05:00
Ben Meadors
4aa6f60e95 Maybe remove pip cache 2024-07-29 06:41:26 -05:00
Ben Meadors
2ffc93324d After 2024-07-29 06:38:34 -05:00
Ben Meadors
c501cc501d Pip pip cheerios plz 2024-07-29 06:37:19 -05:00
Ben Meadors
8c0ff89972 Trunk 2024-07-29 06:14:32 -05:00
Ben Meadors
cf22b7ff04 Latest pip version of setuptools is broken. Install specific version 2024-07-29 06:11:08 -05:00
Ben Meadors
811a9ae261 Macro to trace log all MeshPackets as JSON (#4336)
* Macro to trace log all MeshPackets as JSON

* Comment

* Add trace logging to file for native target

* bytes to hex

* Add time_ms

---------

Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
2024-07-28 19:49:10 -05:00
github-actions[bot]
8b0208d1c6 [create-pull-request] automated change (#4335)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-07-28 14:53:25 -05:00
Jonathan Bennett
1a1d545c38 Adds a userPrefs.h file, default blank, used for default settings for custom builds (#4325)
* add a userPrefs.h file, default blank, which can be used to easily set defaults on custom builds.

* Add Splash Screen to userPrefs

* Add channel 0 defaults to userPrefs.h

* CONFIG_LORA_IGNORE_MQTT_DEFAULT

* Unify naming for USERPREFS defines

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2024-07-28 14:12:30 -05:00
Ben Meadors
32bc2f1137 Add RAK3172 to the STM32WL canon 2024-07-27 09:38:28 -05:00
Ben Meadors
1b249c32bf Copy the actual bin 2024-07-27 07:28:11 -05:00
Ben Meadors
bca9fbe7e4 Missed a needs 2024-07-27 06:49:50 -05:00
Ben Meadors
e70435ebd7 All builds need to only pkg update for their target environment 2024-07-27 06:49:11 -05:00
Ben Meadors
f583837b4e Enable STM32 build (#4339)
* Enable stm32 builds and wio-e5 board

* Chmod
2024-07-27 06:39:16 -05:00
Max
6f235232f0 Added RF95 SX1268 support (#4338) 2024-07-27 05:58:55 -05:00
Nestpebble
8641777bac Add the UF2 conversion script to the p.io task menu (#4337)
* Add the UF2 conversion script to the p.io task menu

Update platformio-custom.py to include the UF2 conversion script as a project task. Saves you dropping into the command line every time.

Tested on Windows only...

* Forgot the build target...
2024-07-26 20:14:31 -05:00
Ben Meadors
4ee15d8128 Trunk 2024-07-26 06:38:54 -05:00
Ben Meadors
394e0e1b3e T1000_E hw model 2024-07-26 06:30:28 -05:00
github-actions[bot]
755952c261 [create-pull-request] automated change (#4333)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2024-07-26 06:29:05 -05:00
Ben Meadors
f645ae943d JSON serialization refactor (#4331) 2024-07-25 20:50:42 -05:00
Thomas Göttgens
4b0bbb8af1 Make STM compile again and update toolchain. (#2960)
* Make STM compile again and update toolchain. The binary is too big for the flash. WIP

* Making progress with OSFS, still WIP

* more progress, still too big. Adding RAK3172 to the equasion

* Make STM compile again and update toolchain. The binary is too big for the flash. WIP

* Making progress with OSFS, still WIP

* more progress, still too big. Adding RAK3172 to the equasion

* still too big

* minimize build

* trunk fmt

* fix a couple of symbol clashes

* trunk fmt

* down to 101% with a release vs. debug build and omitting the flash strings

* fix compilation

* fix compilation once more

* update protobufs linkage

* - Toolchain updated
- Fixed macro error

* silence compiler warning
note: do something about this assert...

* new toolkit and fix Power.cpp

* STM32WL make it fit (#4330)

* Add option to exclude I2C parts

The I2C hals and related code uses a significant amount of flash space and aren't required for a basic node.

* Add option to disable Admin and NodeInfo modules

Disabled by default in minimal build. This saves a significant amount of flash

* Disable unused hals

These use up significant flash

* Add float support for printf for debugging

Makes serial look nice for debugging

* This breaks my build for some reason

* These build flags can save a bit of flash

* Don't disable NodeInfo and Admin modules in minimal build

They fit in flash

* Don't include printf float support by default

Only useful for debugging

---------

Co-authored-by: Adam Lawson <dev@goshawk22.uk>

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Adam Lawson <dev@goshawk22.uk>
2024-07-25 20:16:21 -05:00
Ben Meadors
7ac64bd762 Trunk 2024-07-25 13:09:28 -05:00
Mark Trevor Birss
1481ce987e Fix T1000-E GPS - some changes went missing from #4303? (#4328)
* Update GPS.cpp

* Update GPS.cpp

* Update GPS.cpp

* Update GPS.cpp

* Update GPS.cpp

* Update GPS.cpp

* Update GPS.cpp

* Update GPS.cpp

* Update GPS.cpp
2024-07-25 13:05:03 -05:00
Ben Meadors
c5f2d2736d Whitespace trunk grousing 2024-07-24 21:14:58 -05:00
dylanli
a000a8d347 Support Seeed Tracker-T1000-E (#4303)
* feature-T1000-E: add Added the board definition for T1000-E

-  integrate a script for rapid dependency download that is compatible with both Linux and Windows platforms.
-  add the pin definitions for UART, SPI, GPIO, and other peripherals have been ensured to be correct.
-  add the env configuration for PlatformIO.

* refact-T1000-E: redefine T1000-E board

* feature-T1000-E: add basic sensors

* feature-T1000-E: add button init

* feat: add DRADIOLIB_GODMODE defination for use function setDioAsRfSwitch to DIO LORA RF

* feat : add gps(GNSS_Airoha) sleep mode

* feat: add behavier when rec or send message

* chore: hang IIC bus usage to avoid sensor address conflict

* feat: add sensor data acquisition

* feat : support Airoha GPS

- add disable it in FSM
- update  lookForTime and lookForLocation function

* fix: fix a bug

* version: change version to 0.9.0

* Update tracker-t1000-e.json

Remove a space

* Delete variants/tracker-t1000-e/run_once.sh

Delete not need as we will change platformio.ini

* Update platformio.ini

Update SoftDevice 7.3.0 usage in line with other lr1110 targets

Do we need to keep GODMODE ?

* fix: Button behavier incorrect bug

* fix:remove some invaild code of TextMessageModule

* fix: remove invaild comment

* version: change version to 0.9.1

- update mark's patch
- remove some invaild code and comments
- fix button behavier

* trunk format

* fix:  HELTEC_CAPSULE_SENSOR_V3 block got accidentally deleted

* fix: EnvironmentTelemetry upstream merge went awry.

* fix: Added macro definitions to ensure correct operation of LORA section

* fix :GNSS_AIROHA macro defination in line with others

* fix: upstream backmerge accidentally.

* fix: wrap macro PIN_3V3_EN BUZZER_EN_PIN GNSS_AIROHA in the TRACKER_T1000_E macro guard

---------

Co-authored-by: Mark Trevor Birss <markbirss@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
2024-07-24 21:10:38 -05:00
208 changed files with 4704 additions and 2362 deletions

View File

@@ -11,7 +11,7 @@ runs:
ref: ${{github.event.pull_request.head.ref}}
repository: ${{github.event.pull_request.head.repo.full_name}}
- name: Install dependencies
- name: Install dependencies
shell: bash
run: |
sudo apt-get -y update --fix-missing
@@ -22,19 +22,20 @@ runs:
with:
python-version: 3.x
- name: Cache python libs
uses: actions/cache@v4
id: cache-pip # needed in if test
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip
# - name: Cache python libs
# uses: actions/cache@v4
# id: cache-pip # needed in if test
# with:
# path: ~/.cache/pip
# key: ${{ runner.os }}-pip
- name: Upgrade python tools
shell: bash
run: |
python -m pip install --upgrade pip
pip install -U platformio adafruit-nrfutil
pip install -U meshtastic --pre
pip install -U --no-build-isolation --no-cache-dir "setuptools<72"
pip install -U platformio adafruit-nrfutil --no-build-isolation
pip install -U meshtastic --pre --no-build-isolation
- name: Upgrade platformio
shell: bash

33
.github/workflows/build_stm32.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Build STM32
on:
workflow_call:
inputs:
board:
required: true
type: string
jobs:
build-stm32:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build base
id: base
uses: ./.github/actions/setup-base
- name: Build STM32
run: bin/build-stm32.sh ${{ inputs.board }}
- name: Get release version string
run: echo "version=$(./bin/buildinfo.py long)" >> $GITHUB_OUTPUT
id: version
- name: Store binaries as an artifact
uses: actions/upload-artifact@v4
with:
name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
overwrite: true
path: |
release/*.hex
release/*.bin

View File

@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
arch: [esp32, esp32s3, esp32c3, nrf52840, rp2040, check]
arch: [esp32, esp32s3, esp32c3, nrf52840, rp2040, stm32, check]
runs-on: ubuntu-latest
steps:
- id: checkout
@@ -41,6 +41,7 @@ jobs:
esp32c3: ${{ steps.jsonStep.outputs.esp32c3 }}
nrf52840: ${{ steps.jsonStep.outputs.nrf52840 }}
rp2040: ${{ steps.jsonStep.outputs.rp2040 }}
stm32: ${{ steps.jsonStep.outputs.stm32 }}
check: ${{ steps.jsonStep.outputs.check }}
check:
@@ -103,6 +104,15 @@ jobs:
with:
board: ${{ matrix.board }}
build-stm32:
needs: setup
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.setup.outputs.stm32) }}
uses: ./.github/workflows/build_stm32.yml
with:
board: ${{ matrix.board }}
package-raspbian:
uses: ./.github/workflows/package_raspbian.yml
@@ -134,6 +144,7 @@ jobs:
build-esp32-c3,
build-nrf52,
build-rpi2040,
build-stm32,
package-raspbian,
package-raspbian-armv7l,
package-native,

View File

@@ -0,0 +1,208 @@
/*
* lfs utility functions
*
* Copyright (c) 2017, Arm Limited. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
// MESHTASTIC/@geeksville note: This file is copied from the Adafruit nrf52 arduino lib. And we use a special -include in
// nrf52.ini to load it before EVERY file we do this hack because the default definitions for LFS_ASSERT are quite poor and we
// don't want to fork the adafruit lib (again) and send in a PR that they probably won't merge anyways. This file might break if
// they ever update lfs.util on their side, in which case we'll need to update this file to match their new version. The version
// this is a copy from is almost exactly
// https://github.com/adafruit/Adafruit_nRF52_Arduino/blob/c25d93268a3b9c23e9a1ccfcaf9b208beca624ca/libraries/Adafruit_LittleFS/src/littlefs/lfs_util.h
#ifndef LFS_UTIL_H
#define LFS_UTIL_H
// Users can override lfs_util.h with their own configuration by defining
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
//
// If LFS_CONFIG is used, none of the default utils will be emitted and must be
// provided by the config file. To start I would suggest copying lfs_util.h and
// modifying as needed.
#ifdef LFS_CONFIG
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
#define LFS_STRINGIZE2(x) #x
#include LFS_STRINGIZE(LFS_CONFIG)
#else
// System includes
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#ifndef LFS_NO_MALLOC
#include <stdlib.h>
#endif
#ifndef LFS_NO_ASSERT
#include <assert.h>
#endif
#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR)
#include <stdio.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
// Macros, may be replaced by system specific wrappers. Arguments to these
// macros must not have side-effects as the macros can be removed for a smaller
// code footprint
// Logging functions
#ifndef LFS_NO_DEBUG
void logLegacy(const char *level, const char *fmt, ...);
#define LFS_DEBUG(fmt, ...) logLegacy("DEBUG", "lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
#define LFS_DEBUG(fmt, ...)
#endif
#ifndef LFS_NO_WARN
#define LFS_WARN(fmt, ...) logLegacy("WARN", "lfs warn:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
#define LFS_WARN(fmt, ...)
#endif
#ifndef LFS_NO_ERROR
#define LFS_ERROR(fmt, ...) logLegacy("ERROR", "lfs error:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
#define LFS_ERROR(fmt, ...)
#endif
// Runtime assertions
#ifndef LFS_NO_ASSERT
#define LFS_ASSERT(test) assert(test)
#else
extern void lfs_assert(const char *reason);
#define LFS_ASSERT(test) \
if (!(test)) \
lfs_assert(#test)
#endif
// Builtin functions, these may be replaced by more efficient
// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
// expensive basic C implementation for debugging purposes
// Min/max functions for unsigned 32-bit numbers
static inline uint32_t lfs_max(uint32_t a, uint32_t b)
{
return (a > b) ? a : b;
}
static inline uint32_t lfs_min(uint32_t a, uint32_t b)
{
return (a < b) ? a : b;
}
// Find the next smallest power of 2 less than or equal to a
static inline uint32_t lfs_npw2(uint32_t a)
{
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return 32 - __builtin_clz(a - 1);
#else
uint32_t r = 0;
uint32_t s;
a -= 1;
s = (a > 0xffff) << 4;
a >>= s;
r |= s;
s = (a > 0xff) << 3;
a >>= s;
r |= s;
s = (a > 0xf) << 2;
a >>= s;
r |= s;
s = (a > 0x3) << 1;
a >>= s;
r |= s;
return (r | (a >> 1)) + 1;
#endif
}
// Count the number of trailing binary zeros in a
// lfs_ctz(0) may be undefined
static inline uint32_t lfs_ctz(uint32_t a)
{
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
return __builtin_ctz(a);
#else
return lfs_npw2((a & -a) + 1) - 1;
#endif
}
// Count the number of binary ones in a
static inline uint32_t lfs_popc(uint32_t a)
{
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return __builtin_popcount(a);
#else
a = a - ((a >> 1) & 0x55555555);
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
#endif
}
// Find the sequence comparison of a and b, this is the distance
// between a and b ignoring overflow
static inline int lfs_scmp(uint32_t a, uint32_t b)
{
return (int)(unsigned)(a - b);
}
// Convert from 32-bit little-endian to native order
static inline uint32_t lfs_fromle32(uint32_t a)
{
#if !defined(LFS_NO_INTRINSICS) && ((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return a;
#elif !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && BYTE_ORDER == ORDER_BIG_ENDIAN) || (defined(__BYTE_ORDER) && __BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return __builtin_bswap32(a);
#else
return (((uint8_t *)&a)[0] << 0) | (((uint8_t *)&a)[1] << 8) | (((uint8_t *)&a)[2] << 16) | (((uint8_t *)&a)[3] << 24);
#endif
}
// Convert to 32-bit little-endian from native order
static inline uint32_t lfs_tole32(uint32_t a)
{
return lfs_fromle32(a);
}
// Calculate CRC-32 with polynomial = 0x04c11db7
void lfs_crc(uint32_t *crc, const void *buffer, size_t size);
// Allocate memory, only used if buffers are not provided to littlefs
static inline void *lfs_malloc(size_t size)
{
#ifndef LFS_NO_MALLOC
extern void *pvPortMalloc(size_t xWantedSize);
return pvPortMalloc(size);
#else
(void)size;
return NULL;
#endif
}
// Deallocate memory, only used if buffers are not provided to littlefs
static inline void lfs_free(void *p)
{
#ifndef LFS_NO_MALLOC
extern void vPortFree(void *pv);
vPortFree(p);
#else
(void)p;
#endif
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif
#endif

View File

@@ -2,9 +2,13 @@
; Instead of the standard nordicnrf52 platform, we use our fork which has our added variant files
platform = platformio/nordicnrf52@^10.5.0
extends = arduino_base
platform_packages =
; our custom Git version until they merge our PR
framework-arduinoadafruitnrf52 @ https://github.com/geeksville/Adafruit_nRF52_Arduino.git
build_type = debug
build_flags =
build_flags =
-include arch/nrf52/cpp_overrides/lfs_util.h
${arduino_base.build_flags}
-DSERIAL_BUFFER_SIZE=1024
-Wno-unused-variable

View File

@@ -7,3 +7,72 @@ lib_deps =
${nrf52_base.lib_deps}
${environmental_base.lib_deps}
https://github.com/Kongduino/Adafruit_nRFCrypto.git#e31a8825ea3300b163a0a3c1ddd5de34e10e1371
; Common NRF52 debugging settings follow. See the Meshtastic developer docs for how to connect SWD debugging probes to your board.
; We want the initial breakpoint at setup() instead of main(). Also we want to enable semihosting at that point so instead of
debug_init_break = tbreak setup
; we just turn off the platformio tbreak and do it in .gdbinit (where we have more flexibility for scripting)
; also we use a permanent breakpoint so it gets reused each time we restart the debugging session?
; debug_init_break = tbreak main
; Note: add "monitor arm semihosting_redirect tcp 4444 all" if you want the stdout from the device to go to that port number instead
; (for use by meshtastic command line)
; monitor arm semihosting disable
; monitor debug_level 3
;
; IMPORTANT: fileio must be disabled before using port 5555 - openocd ver 0.12 has a bug where if enabled it never properly parses the special :tt name
; for stdio access.
; monitor arm semihosting_redirect tcp 5555 stdio
; Also note: it is _impossible_ to do non blocking reads on the semihost console port (an oversight when ARM specified the semihost API).
; So we'll neve be able to general purpose bi-directional communication with the device over semihosting.
debug_extra_cmds =
echo Running .gdbinit script
;monitor arm semihosting enable
;monitor arm semihosting_fileio enable
;monitor arm semihosting_redirect disable
commands 1
; echo Breakpoint at setup() has semihosting console, connect to it with "telnet localhost 5555"
; set wantSemihost = 1
set useSoftDevice = 0
end
; Only reprogram the board if the code has changed
debug_load_mode = modified
;debug_load_mode = manual
; We default to the stlink adapter because it is very cheap and works well, though others (such as jlink) are also supported.
;debug_tool = jlink
debug_tool = stlink
debug_speed = 4000
;debug_tool = custom
; debug_server =
; openocd
; -f
; /usr/local/share/openocd/scripts/interface/stlink.cfg
; -f
; /usr/local/share/openocd/scripts/target/nrf52.cfg
; $PLATFORMIO_CORE_DIR/packages/tool-openocd/openocd/scripts/interface/cmsis-dap.cfg
; Allows programming and debug via the RAK NanoDAP as the default debugger tool for the RAK4631 (it is only $10!)
; programming time is about the same as the bootloader version.
; For information on this see the meshtastic developers documentation for "Development on the NRF52"
; We manually pass in the elf file so that pyocd can reverse engineer FreeRTOS data (running threads, etc...)
;debug_server =
; pyocd
; gdbserver
; -j
; ${platformio.workspace_dir}/..
; -t
; nrf52840
; --semihosting
; --elf
; ${platformio.build_dir}/${this.__env__}/firmware.elf
; If you want to debug the semihosting support you can turn on extra logging in pyocd with
; -L
; pyocd.debug.semihost.trace=debug
; The following is not needed because it automatically tries do this
;debug_server_ready_pattern = -.*GDB server started on port \d+.*
;debug_port = localhost:3333

37
arch/stm32/stm32.ini Normal file
View File

@@ -0,0 +1,37 @@
[stm32_base]
extends = arduino_base
platform = ststm32
platform_packages = platformio/framework-arduinoststm32@https://github.com/stm32duino/Arduino_Core_STM32.git#361a7fdb67e2a7104e99b4f42a802469eef8b129
build_type = release
;board_build.flash_offset = 0x08000000
build_flags =
${arduino_base.build_flags}
-flto
-Isrc/platform/stm32wl -g
-DMESHTASTIC_MINIMIZE_BUILD
-DMESHTASTIC_EXCLUDE_GPS
-DDEBUG_MUTE
; -DVECT_TAB_OFFSET=0x08000000
-DconfigUSE_CMSIS_RTOS_V2=1
; -DSPI_MODE_0=SPI_MODE0
-fmerge-all-constants
-ffunction-sections
-fdata-sections
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/RemoteHardwareModule.cpp> -<platform/nrf52> -<platform/portduino> -<platform/rp2040> -<mesh/raspihttp>
board_upload.offset_address = 0x08000000
upload_protocol = stlink
lib_deps =
${env.lib_deps}
charlesbaynham/OSFS@^1.2.3
https://github.com/caveman99/Crypto.git#f61ae26a53f7a2d0ba5511625b8bf8eff3a35d5e
lib_ignore =
https://github.com/mathertel/OneButton@~2.6.1
Wire

View File

@@ -1,28 +0,0 @@
[stm32wl5e_base]
platform_packages = platformio/framework-arduinoststm32 @ https://github.com/stm32duino/Arduino_Core_STM32.git#6e3f9910d0122e82a6c3438507dfac3d2fd80a39
platform = ststm32
board = generic_wl5e
framework = arduino
build_type = debug
build_flags =
${arduino_base.build_flags}
-Isrc/platform/stm32wl -g
-DconfigUSE_CMSIS_RTOS_V2=1
-DVECT_TAB_OFFSET=0x08000000
build_src_filter =
${arduino_base.build_src_filter} -<platform/esp32/> -<nimble/> -<mesh/api/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mesh/eth/> -<input> -<buzz> -<modules/Telemetry> -<platform/nrf52> -<platform/portduino> -<platform/rp2040> -<mesh/raspihttp>
board_upload.offset_address = 0x08000000
upload_protocol = stlink
lib_deps =
${env.lib_deps}
https://github.com/kokke/tiny-AES-c.git#f06ac37fc31dfdaca2e0d9bec83f90d5663c319b
https://github.com/littlefs-project/littlefs.git#v2.5.1
https://github.com/stm32duino/STM32FreeRTOS.git#10.3.1
lib_ignore =
mathertel/OneButton

View File

@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
platformio pkg update
platformio pkg update -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*

View File

@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
platformio pkg update
platformio pkg update -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*

View File

@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
platformio pkg update
platformio pkg update -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*

29
bin/build-stm32.sh Executable file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -e
VERSION=$(bin/buildinfo.py long)
SHORT_VERSION=$(bin/buildinfo.py short)
OUTDIR=release/
rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
platformio pkg update -e $1
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.*
# The shell vars the build tool expects to find
export APP_VERSION=$VERSION
basename=firmware-$1-$VERSION
pio run --environment $1 # -v
SRCELF=.pio/build/$1/firmware.elf
cp $SRCELF $OUTDIR/$basename.elf
SRCBIN=.pio/build/$1/firmware.bin
cp $SRCBIN $OUTDIR/$basename.bin

View File

@@ -54,6 +54,8 @@ Lora:
# ch341_quirk: true # Uncomment this to use the chunked SPI transfer that seems to fix the ch341
# spiSpeed: 2000000
### Set gpio chip to use in /dev/. Defaults to 0.
### Notably the Raspberry Pi 5 puts the GPIO header on gpiochip4
# gpiochip: 4
@@ -111,6 +113,9 @@ Display:
# Height: 320
# Rotate: true
### You can also specify the spi device for the display to use
# spidev: spidev0.0
Touchscreen:
### Note, at least for now, the touchscreen must have a CS pin defined, even if you let Linux manage the CS switching.
@@ -126,15 +131,21 @@ Touchscreen:
# CS: 7
# IRQ: 17
### Configure device for direct keyboard input
### You can also specify the spi device for the touchscreen to use
# spidev: spidev0.0
Input:
### Configure device for direct keyboard input
# KeyboardDevice: /dev/input/by-id/usb-_Raspberry_Pi_Internal_Keyboard-event-kbd
###
Logging:
LogLevel: info # debug, info, warn, error
# TraceFile: /var/log/meshtasticd.json
# AsciiLogs: true # default if not specified is !isatty() on stdout
Webserver:
# Port: 443 # Port for Webserver & Webservices
@@ -142,3 +153,4 @@ Webserver:
General:
MaxNodes: 200
MaxMessageQueue: 100

View File

@@ -1,3 +1,4 @@
#!/usr/bin/env python3
# trunk-ignore-all(ruff/F821)
# trunk-ignore-all(flake8/F821): For SConstruct imports
import sys
@@ -78,6 +79,11 @@ if platform.name == "espressif32":
# For newer ESP32 targets, using newlib nano works better.
env.Append(LINKFLAGS=["--specs=nano.specs", "-u", "_printf_float"])
if platform.name == "nordicnrf52":
env.AddPostAction("$BUILD_DIR/${PROGNAME}.hex",
env.VerboseAction(f"{sys.executable} ./bin/uf2conv.py $BUILD_DIR/firmware.hex -c -f 0xADA52840 -o $BUILD_DIR/firmware.uf2",
"Generating UF2 file"))
Import("projenv")
prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"

View File

@@ -0,0 +1,30 @@
# tips from mark on how to replace the (broken) bootloader on the red wio_tracker_1110 boards.
~/.platformio/penv/bin/adafruit-nrfutil --verbose dfu serial --package wio_tracker_1110_bootloader-0.9.1_s140_7.3.0.zip -p /dev/ttyACM1 -b 115200 --singlebank --touch 1200
exit
# Output should look like
Upgrading target on /dev/ttyACM1 with DFU package /home/kevinh/development/meshtastic/WioWM1110/wio_tracker_1110_bootloader-0.9.1_s140_7.3.0.zip. Flow control is disabled, Single bank, Touch 1200
Touched serial port /dev/ttyACM1
Opened serial port /dev/ttyACM1
Starting DFU upgrade of type 3, SoftDevice size: 152728, bootloader size: 39000, application size: 0
Sending DFU start packet
Sending DFU init packet
Sending firmware file
########################################
########################################
########################################
########################################
########################################
########################################
########################################
########################################
########################################
###############
Activating new firmware
DFU upgrade took 20.242434978485107s
Device programmed.

View File

@@ -0,0 +1,42 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"partitions": "default_8MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
["0x303A", "0x1001"],
["0x303A", "0x0002"]
],
"mcu": "esp32s3",
"variant": "heltec_vision_master_e290"
},
"connectivity": ["wifi", "bluetooth", "lora"],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "Heltec Vision Master E290",
"upload": {
"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
},
"url": "https://heltec.org/project/vision-master-e290/",
"vendor": "Heltec"
}

View File

@@ -0,0 +1,42 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"partitions": "default_8MB.csv"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_USB_MODE=0",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [
["0x303A", "0x1001"],
["0x303A", "0x0002"]
],
"mcu": "esp32s3",
"variant": "heltec_vision_master_t190"
},
"connectivity": ["wifi", "bluetooth", "lora"],
"debug": {
"openocd_target": "esp32s3.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "Heltec Vision Master t190",
"upload": {
"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
},
"url": "https://heltec.org/project/vision-master-t190/",
"vendor": "Heltec"
}

View File

@@ -0,0 +1,58 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v7.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x239A", "0x802A"]
],
"usb_product": "ME25LS01-BOOT",
"mcu": "nrf52840",
"variant": "MINEWSEMI_ME25LS01",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "7.3.0",
"sd_fwid": "0x0123"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd"
},
"frameworks": ["arduino"],
"name": "Minesemi ME25LS01",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink",
"cmsis-dap",
"blackmagic"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://en.minewsemi.com/lora-module/lr1110-nrf52840-me25LS01l",
"vendor": "Minesemi"
}

View File

@@ -0,0 +1,58 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v7.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_WIO_WM1110 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8029"],
["0x239A", "0x0029"],
["0x239A", "0x002A"],
["0x239A", "0x802A"]
],
"usb_product": "T1000-E-BOOT",
"mcu": "nrf52840",
"variant": "Seeed_T1000-E",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "7.3.0",
"sd_fwid": "0x0123"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd"
},
"frameworks": ["arduino"],
"name": "Seeed T1000-E",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink",
"cmsis-dap",
"blackmagic"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://www.seeedstudio.com/SenseCAP-Card-Tracker-T1000-E-for-Meshtastic-p-5913.html",
"vendor": "Seeed Studio"
}

View File

@@ -1,5 +1,8 @@
{
"build": {
"arduino": {
"variant_h": "variant_RAK3172_MODULE.h"
},
"core": "stm32",
"cpu": "cortex-m4",
"extra_flags": "-DSTM32WLxx -DSTM32WLE5xx -DARDUINO_GENERIC_WLE5CCUX",

View File

@@ -5,16 +5,24 @@ Import("env")
# NOTE: This is not currently used, but can serve as an example on how to write extra_scripts
print("Current CLI targets", COMMAND_LINE_TARGETS)
print("Current Build targets", BUILD_TARGETS)
print("CPP defs", env.get("CPPDEFINES"))
# print("Current CLI targets", COMMAND_LINE_TARGETS)
# print("Current Build targets", BUILD_TARGETS)
# print("CPP defs", env.get("CPPDEFINES"))
# print(env.Dump())
# Adafruit.py in the platformio build tree is a bit naive and always enables their USB stack for building. We don't want this.
# So come in after that python script has run and disable it. This hack avoids us having to fork that big project and send in a PR
# which might not be accepted. -@geeksville
env["CPPDEFINES"].remove("USBCON")
env["CPPDEFINES"].remove("USE_TINYUSB")
lib_builders = env.get("__PIO_LIB_BUILDERS", None)
if lib_builders is not None:
print("Disabling Adafruit USB stack")
for k in lib_builders:
if k.name == "Adafruit TinyUSB Library":
libenv = k.env
# print(f"{k.name }: { libenv.Dump() } ")
# libenv["CPPDEFINES"].remove("USBCON")
libenv["CPPDEFINES"].remove("USE_TINYUSB")
# Custom actions when building program/firmware
# env.AddPreAction("buildprog", callback...)

View File

@@ -18,10 +18,7 @@ import subprocess
import sys
from platformio.project.exception import PlatformioException
from platformio.public import (
DeviceMonitorFilterBase,
load_build_metadata,
)
from platformio.public import DeviceMonitorFilterBase, load_build_metadata
# By design, __init__ is called inside miniterm and we can't pass context to it.
# pylint: disable=attribute-defined-outside-init
@@ -32,7 +29,7 @@ IS_WINDOWS = sys.platform.startswith("win")
class Esp32C3ExceptionDecoder(DeviceMonitorFilterBase):
NAME = "esp32_c3_exception_decoder"
PCADDR_PATTERN = re.compile(r'0x4[0-9a-f]{7}', re.IGNORECASE)
PCADDR_PATTERN = re.compile(r"0x4[0-9a-f]{7}", re.IGNORECASE)
def __call__(self):
self.buffer = ""
@@ -75,14 +72,14 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html
% self.__class__.__name__
)
return False
if not os.path.isfile(self.addr2line_path):
sys.stderr.write(
"%s: disabling, addr2line at %s does not exist\n"
% (self.__class__.__name__, self.addr2line_path)
)
return False
return True
except PlatformioException as e:
sys.stderr.write(
@@ -117,7 +114,7 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html
trace = self.get_backtrace(m)
if len(trace) != "":
text = text[: last] + trace + text[last :]
text = text[:last] + trace + text[last:]
last += len(trace)
return text
@@ -125,14 +122,10 @@ See https://docs.platformio.org/page/projectconf/build_configurations.html
def get_backtrace(self, match):
trace = "\n"
enc = "mbcs" if IS_WINDOWS else "utf-8"
args = [self.addr2line_path, u"-fipC", u"-e", self.firmware_path]
args = [self.addr2line_path, "-fipC", "-e", self.firmware_path]
try:
addr = match.group()
output = (
subprocess.check_output(args + [addr])
.decode(enc)
.strip()
)
output = subprocess.check_output(args + [addr]).decode(enc).strip()
output = output.replace(
"\n", "\n "
) # newlines happen with inlined methods

View File

@@ -86,7 +86,7 @@ monitor_filters = direct
lib_deps =
jgromes/RadioLib@~6.6.0
https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 ; ESP8266_SSD1306
mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
https://github.com/mathertel/OneButton@~2.6.1 ; OneButton library for non-blocking button debounce
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4
https://github.com/meshtastic/ArduinoThread.git#1ae8778c85d0a2a729f989e0b1e7d7c4dc84eef0
@@ -155,4 +155,4 @@ lib_deps =
mprograms/QMC5883LCompass@^1.2.0
https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee
https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee

View File

@@ -29,7 +29,6 @@ volatile ButtonThread::ButtonEventType ButtonThread::btnEvent = ButtonThread::BU
#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO)
OneButton ButtonThread::userButton; // Get reference to static member
#endif
ButtonThread::ButtonThread() : OSThread("Button")
{
#if defined(BUTTON_PIN) || defined(ARCH_PORTDUINO)
@@ -43,7 +42,7 @@ ButtonThread::ButtonThread() : OSThread("Button")
int pin = config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN; // Resolved button pin
#if defined(HELTEC_CAPSULE_SENSOR_V3)
this->userButton = OneButton(pin, false, false);
#elif defined(BUTTON_ACTIVE_LOW) // change by WayenWeng
#elif defined(BUTTON_ACTIVE_LOW)
this->userButton = OneButton(pin, BUTTON_ACTIVE_LOW, BUTTON_ACTIVE_PULLUP);
#else
this->userButton = OneButton(pin, true, true);
@@ -53,7 +52,7 @@ ButtonThread::ButtonThread() : OSThread("Button")
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
#ifdef BUTTON_SENSE_TYPE // change by WayenWeng
#ifdef BUTTON_SENSE_TYPE
pinMode(pin, BUTTON_SENSE_TYPE);
#else
pinMode(pin, INPUT_PULLUP_SENSE);
@@ -145,8 +144,8 @@ int32_t ButtonThread::runOnce()
case BUTTON_EVENT_DOUBLE_PRESSED: {
LOG_BUTTON("Double press!\n");
service.refreshLocalMeshNode();
auto sentPosition = service.trySendPosition(NODENUM_BROADCAST, true);
service->refreshLocalMeshNode();
auto sentPosition = service->trySendPosition(NODENUM_BROADCAST, true);
if (screen) {
if (sentPosition)
screen->print("Sent ad-hoc position\n");
@@ -225,7 +224,6 @@ int32_t ButtonThread::runOnce()
btnEvent = BUTTON_EVENT_NONE;
}
runASAP = false;
return 50;
}

View File

@@ -13,7 +13,7 @@
#endif
#ifndef BUTTON_TOUCH_MS
#define BUTTON_TOCH_MS 400
#define BUTTON_TOUCH_MS 400
#endif
class ButtonThread : public concurrency::OSThread

View File

@@ -26,6 +26,20 @@ SOFTWARE.*/
#include "DebugConfiguration.h"
#ifdef ARCH_PORTDUINO
#include "platform/portduino/PortduinoGlue.h"
#endif
/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic
extern "C" void logLegacy(const char *level, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (console)
console->vprintf(level, fmt, args);
va_end(args);
}
#if HAS_NETWORKING
Syslog::Syslog(UDP &client)
@@ -129,6 +143,11 @@ bool Syslog::vlogf(uint16_t pri, const char *appName, const char *fmt, va_list a
inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *message)
{
int result;
#ifdef ARCH_PORTDUINO
bool utf = !settingsMap[ascii_logs];
#else
bool utf = true;
#endif
if (!this->_enabled)
return false;
@@ -159,7 +178,12 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess
this->_client->print(this->_deviceHostname);
this->_client->print(' ');
this->_client->print(appName);
this->_client->print(F(" - - - \xEF\xBB\xBF"));
this->_client->print(F(" - - - "));
if (utf) {
this->_client->print(F("\xEF\xBB\xBF"));
} else {
this->_client->print(F(" "));
}
this->_client->print(F("["));
this->_client->print(int(millis() / 1000));
this->_client->print(F("]: "));
@@ -169,4 +193,4 @@ inline bool Syslog::_sendLog(uint16_t pri, const char *appName, const char *mess
return true;
}
#endif
#endif

View File

@@ -3,8 +3,8 @@
#include "configuration.h"
// DEBUG LED
#ifndef LED_INVERTED
#define LED_INVERTED 0 // define as 1 if LED is active low (on)
#ifndef LED_STATE_ON
#define LED_STATE_ON 1
#endif
// -----------------------------------------------------------------------------
@@ -62,6 +62,9 @@
#endif
#endif
/// A C wrapper for LOG_DEBUG that can be used from arduino C libs that don't know about C++ or meshtastic
extern "C" void logLegacy(const char *level, const char *fmt, ...);
#define SYSLOG_NILVALUE "-"
#define SYSLOG_CRIT 2 /* critical conditions */

View File

@@ -24,6 +24,39 @@ SPIClass SPI1(HSPI);
#endif // HAS_SDCARD
#if defined(ARCH_STM32WL)
uint16_t OSFS::startOfEEPROM = 1;
uint16_t OSFS::endOfEEPROM = 2048;
// 3) How do I read from the medium?
void OSFS::readNBytes(uint16_t address, unsigned int num, byte *output)
{
for (uint16_t i = address; i < address + num; i++) {
*output = EEPROM.read(i);
output++;
}
}
// 4) How to I write to the medium?
void OSFS::writeNBytes(uint16_t address, unsigned int num, const byte *input)
{
for (uint16_t i = address; i < address + num; i++) {
EEPROM.update(i, *input);
input++;
}
}
#endif
bool lfs_assert_failed =
false; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our modified lfs_util.h)
extern "C" void lfs_assert(const char *reason)
{
LOG_ERROR("LFS assert: %s\n", reason);
lfs_assert_failed = true;
}
/**
* @brief Copies a file from one location to another.
*
@@ -33,7 +66,33 @@ SPIClass SPI1(HSPI);
*/
bool copyFile(const char *from, const char *to)
{
#ifdef FSCom
#ifdef ARCH_STM32WL
unsigned char cbuffer[2048];
// Var to hold the result of actions
OSFS::result r;
r = OSFS::getFile(from, cbuffer);
if (r == notfound) {
LOG_ERROR("Failed to open source file %s\n", from);
return false;
} else if (r == noerr) {
r = OSFS::newFile(to, cbuffer, true);
if (r == noerr) {
return true;
} else {
LOG_ERROR("OSFS Error %d\n", r);
return false;
}
} else {
LOG_ERROR("OSFS Error %d\n", r);
return false;
}
return true;
#elif defined(FSCom)
unsigned char cbuffer[16];
File f1 = FSCom.open(from, FILE_O_READ);
@@ -70,7 +129,13 @@ bool copyFile(const char *from, const char *to)
*/
bool renameFile(const char *pathFrom, const char *pathTo)
{
#ifdef FSCom
#ifdef ARCH_STM32WL
if (copyFile(pathFrom, pathTo) && (OSFS::deleteFile(pathFrom) == OSFS::result::NO_ERROR)) {
return true;
} else {
return false;
}
#elif defined(FSCom)
#ifdef ARCH_ESP32
// rename was fixed for ESP32 IDF LittleFS in April
return FSCom.rename(pathFrom, pathTo);
@@ -143,7 +208,7 @@ std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels)
* @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)
void listDir(const char *dirname, uint8_t levels, bool del)
{
#ifdef FSCom
#if (defined(ARCH_ESP32) || defined(ARCH_RP2040) || defined(ARCH_PORTDUINO))
@@ -158,7 +223,9 @@ void listDir(const char *dirname, uint8_t levels, bool del = false)
}
File file = root.openNextFile();
while (file) {
while (
file &&
file.name()[0]) { // This file.name() check is a workaround for a bug in the Adafruit LittleFS nrf52 glue (see issue 4395)
if (file.isDirectory() && !String(file.name()).endsWith(".")) {
if (levels) {
#ifdef ARCH_ESP32
@@ -257,62 +324,6 @@ void rmDir(const char *dirname)
#endif
}
bool fsCheck()
{
#if defined(ARCH_NRF52)
size_t write_size = 0;
size_t read_size = 0;
char buf[32] = {0};
Adafruit_LittleFS_Namespace::File file(FSCom);
const char *text = "meshtastic fs test";
size_t text_length = strlen(text);
const char *filename = "/meshtastic.txt";
LOG_DEBUG("Try create file .\n");
if (file.open(filename, FILE_O_WRITE)) {
write_size = file.write(text);
} else {
LOG_DEBUG("Open file failed .\n");
goto FORMAT_FS;
}
if (write_size != text_length) {
LOG_DEBUG("Text bytes do not match .\n");
file.close();
goto FORMAT_FS;
}
file.close();
if (!file.open(filename, FILE_O_READ)) {
LOG_DEBUG("Open file failed .\n");
goto FORMAT_FS;
}
read_size = file.readBytes(buf, text_length);
if (read_size != text_length) {
LOG_DEBUG("Text bytes do not match .\n");
file.close();
goto FORMAT_FS;
}
if (memcmp(buf, text, text_length) != 0) {
LOG_DEBUG("The written bytes do not match the read bytes .\n");
file.close();
goto FORMAT_FS;
}
return true;
FORMAT_FS:
LOG_DEBUG("Format FS ....\n");
FSCom.format();
FSCom.begin();
return false;
#else
return true;
#endif
}
void fsInit()
{
#ifdef FSCom
@@ -322,35 +333,6 @@ void fsInit()
}
#if defined(ARCH_ESP32)
LOG_DEBUG("Filesystem files (%d/%d Bytes):\n", FSCom.usedBytes(), FSCom.totalBytes());
#elif defined(ARCH_NRF52)
/*
* nRF52840 has a certain chance of automatic formatting failure.
* Try to create a file after initializing the file system. If the creation fails,
* it means that the file system is not working properly. Please format it manually again.
* To check the normality of the file system, you need to disable the LFS_NO_ASSERT assertion.
* Otherwise, the assertion will be entered at the moment of reading or opening, and the FS will not be formatted.
* */
bool ret = false;
uint8_t retry = 3;
while (retry--) {
ret = fsCheck();
if (ret) {
LOG_DEBUG("File system check is OK.\n");
break;
}
delay(10);
}
// It may not be possible to reach this step.
// Add a loop here to prevent unpredictable situations from happening.
// Can add a screen to display error status later.
if (!ret) {
while (1) {
LOG_ERROR("The file system is damaged and cannot proceed to the next step.\n");
delay(1000);
}
}
#else
LOG_DEBUG("Filesystem files:\n");
#endif

View File

@@ -15,10 +15,13 @@
#endif
#if defined(ARCH_STM32WL)
#include "platform/stm32wl/InternalFileSystem.h" // STM32WL version
#define FSCom InternalFS
#define FSBegin() FSCom.begin()
using namespace LittleFS_Namespace;
// STM32WL series 2 Kbytes (8 rows of 256 bytes)
#include <EEPROM.h>
#include <OSFS.h>
// Useful consts
const OSFS::result noerr = OSFS::result::NO_ERROR;
const OSFS::result notfound = OSFS::result::FILE_NOT_FOUND;
#endif
#if defined(ARCH_RP2040)
@@ -48,9 +51,13 @@ using namespace Adafruit_LittleFS_Namespace;
#endif
void fsInit();
void fsListFiles();
bool copyFile(const char *from, const char *to);
bool renameFile(const char *pathFrom, const char *pathTo);
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels);
void listDir(const char *dirname, uint8_t levels, bool del);
void listDir(const char *dirname, uint8_t levels, bool del = false);
void rmDir(const char *dirname);
void setupSDCard();
void setupSDCard();
extern bool lfs_assert_failed; // Note: we use this global on all platforms, though it can only be set true on nrf52 (in our
// modified lfs_util.h)

84
src/GpioLogic.cpp Normal file
View File

@@ -0,0 +1,84 @@
#include "GpioLogic.h"
#include <assert.h>
void GpioVirtPin::set(bool value)
{
if (value != this->value) {
this->value = value ? PinState::On : PinState::Off;
if (dependentPin)
dependentPin->update();
}
}
GpioTransformer::GpioTransformer(GpioPin *outPin) : outPin(outPin) {}
void GpioTransformer::set(bool value)
{
outPin->set(value);
}
GpioNotTransformer::GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin) : GpioTransformer(outPin), inPin(inPin)
{
assert(!inPin->dependentPin); // We only allow one dependent pin
inPin->dependentPin = this;
// Don't update at construction time, because various GpioPins might be global constructor based not yet initied because
// order of operations for global constructors is not defined.
// update();
}
/**
* Update the output pin based on the current state of the input pin.
*/
void GpioNotTransformer::update()
{
auto p = inPin->get();
if (p == GpioVirtPin::PinState::Unset)
return; // Not yet fully initialized
set(!p);
}
GpioBinaryTransformer::GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation)
: GpioTransformer(outPin), inPin1(inPin1), inPin2(inPin2), operation(operation)
{
assert(!inPin1->dependentPin); // We only allow one dependent pin
inPin1->dependentPin = this;
assert(!inPin2->dependentPin); // We only allow one dependent pin
inPin2->dependentPin = this;
// Don't update at construction time, because various GpioPins might be global constructor based not yet initied because
// order of operations for global constructors is not defined.
// update();
}
void GpioBinaryTransformer::update()
{
auto p1 = inPin1->get(), p2 = inPin2->get();
GpioVirtPin::PinState newValue = GpioVirtPin::PinState::Unset;
if (p1 == GpioVirtPin::PinState::Unset)
newValue = p2; // Not yet fully initialized
else if (p2 == GpioVirtPin::PinState::Unset)
newValue = p1; // Not yet fully initialized
// If we've already found our value just use it, otherwise need to do the operation
if (newValue == GpioVirtPin::PinState::Unset) {
switch (operation) {
case And:
newValue = (GpioVirtPin::PinState)(p1 && p2);
break;
case Or:
newValue = (GpioVirtPin::PinState)(p1 || p2);
break;
case Xor:
newValue = (GpioVirtPin::PinState)(p1 != p2);
break;
default:
assert(false);
}
}
set(newValue);
}
GpioSplitter::GpioSplitter(GpioPin *outPin1, GpioPin *outPin2) : outPin1(outPin1), outPin2(outPin2) {}

144
src/GpioLogic.h Normal file
View File

@@ -0,0 +1,144 @@
#pragma once
#include "configuration.h"
/**This is a set of classes to mediate access to GPIOs in a structured way. Most usage of GPIOs do not
require these classes! But if your hardware has a GPIO that is 'shared' between multiple devices (i.e. a shared power enable)
then using these classes might be able to let you cleanly turn on that enable when either dependent device is needed.
Note: these classes are intended to be 99% inline for the common case so should have minimal impact on flash or RAM
requirements.
*/
/**
* A logical GPIO pin (not necessary raw hardware).
*/
class GpioPin
{
public:
virtual void set(bool value) = 0;
};
/**
* A physical GPIO hw pin.
*/
class GpioHwPin : public GpioPin
{
uint32_t num;
public:
explicit GpioHwPin(uint32_t num) : num(num) {}
void set(bool value) { digitalWrite(num, value); }
};
class GpioTransformer;
class GpioNotTransformer;
class GpioBinaryTransformer;
/**
* A virtual GPIO pin.
*/
class GpioVirtPin : public GpioPin
{
friend class GpioBinaryTransformer;
friend class GpioNotTransformer;
public:
enum PinState { On = true, Off = false, Unset = 2 };
void set(bool value);
PinState get() const { return value; }
private:
PinState value = PinState::Unset;
GpioTransformer *dependentPin = NULL;
};
#include <assert.h>
/**
* A 'smart' trigger that can depend in a fake GPIO and if that GPIO changes, drive some other downstream GPIO to change.
* notably: the set method is not public (because it always is calculated by a subclass)
*/
class GpioTransformer
{
public:
/**
* Update the output pin based on the current state of the input pin.
*/
virtual void update() = 0;
protected:
GpioTransformer(GpioPin *outPin);
void set(bool value);
private:
GpioPin *outPin;
};
/**
* A transformer that performs a unary NOT operation from an input.
*/
class GpioNotTransformer : public GpioTransformer
{
public:
GpioNotTransformer(GpioVirtPin *inPin, GpioPin *outPin);
protected:
friend class GpioVirtPin;
/**
* Update the output pin based on the current state of the input pin.
*/
void update();
private:
GpioVirtPin *inPin;
};
/**
* A transformer that combines multiple virtual pins to drive an output pin
*/
class GpioBinaryTransformer : public GpioTransformer
{
public:
enum Operation { And, Or, Xor };
GpioBinaryTransformer(GpioVirtPin *inPin1, GpioVirtPin *inPin2, GpioPin *outPin, Operation operation);
protected:
friend class GpioVirtPin;
/**
* Update the output pin based on the current state of the input pins.
*/
void update();
private:
GpioVirtPin *inPin1;
GpioVirtPin *inPin2;
Operation operation;
};
/**
* Sometimes a single output GPIO single needs to drive multiple physical GPIOs. This class provides that.
*/
class GpioSplitter : public GpioPin
{
public:
GpioSplitter(GpioPin *outPin1, GpioPin *outPin2);
void set(bool value)
{
outPin1->set(value);
outPin2->set(value);
}
private:
GpioPin *outPin1;
GpioPin *outPin2;
};

66
src/Led.cpp Normal file
View File

@@ -0,0 +1,66 @@
#include "Led.h"
#include "PowerMon.h"
#include "main.h"
#include "power.h"
GpioVirtPin ledForceOn, ledBlink;
#if defined(LED_PIN)
// Most boards have a GPIO for LED control
static GpioHwPin ledRawHwPin(LED_PIN);
#else
static GpioVirtPin ledRawHwPin; // Dummy pin for no hardware
#endif
#if LED_STATE_ON == 0
static GpioVirtPin ledHwPin;
static GpioNotTransformer ledInverter(&ledHwPin, &ledRawHwPin);
#else
static GpioPin &ledHwPin = ledRawHwPin;
#endif
#if defined(HAS_PMU)
/**
* A GPIO controlled by the PMU
*/
class GpioPmuPin : public GpioPin
{
public:
void set(bool value)
{
if (pmu_found && PMU) {
// blink the axp led
PMU->setChargingLedMode(value ? XPOWERS_CHG_LED_ON : XPOWERS_CHG_LED_OFF);
}
}
} ledPmuHwPin;
// In some cases we need to drive a PMU LED and a normal LED
static GpioSplitter ledFinalPin(&ledHwPin, &ledPmuHwPin);
#else
static GpioPin &ledFinalPin = ledHwPin;
#endif
#ifdef USE_POWERMON
/**
* We monitor changes to the LED drive output because we use that as a sanity test in our power monitor stuff.
*/
class MonitoredLedPin : public GpioPin
{
public:
void set(bool value)
{
if (powerMon) {
if (value)
powerMon->setState(meshtastic_PowerMon_State_LED_On);
else
powerMon->clearState(meshtastic_PowerMon_State_LED_On);
}
ledFinalPin.set(value);
}
} monitoredLedPin;
#else
static GpioPin &monitoredLedPin = ledFinalPin;
#endif
static GpioBinaryTransformer ledForcer(&ledForceOn, &ledBlink, &monitoredLedPin, GpioBinaryTransformer::Or);

7
src/Led.h Normal file
View File

@@ -0,0 +1,7 @@
#include "GpioLogic.h"
#include "configuration.h"
/**
* ledForceOn and ledForceOff both override the normal ledBlinker behavior (which is controlled by main)
*/
extern GpioVirtPin ledForceOn, ledBlink;

View File

@@ -1,60 +0,0 @@
#include "OSTimer.h"
#include "configuration.h"
/**
* Schedule a callback to run. The callback must _not_ block, though it is called from regular thread level (not ISR)
*
* NOTE! xTimerPend... seems to ignore the time passed in on ESP32 and on NRF52
* The reason this didn't work is because xTimerPednFunctCall really isn't a timer function at all - it just means run the
callback
* from the timer thread the next time you have spare cycles.
*
* @return true if successful, false if the timer fifo is too full.
bool scheduleOSCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec)
{
return xTimerPendFunctionCall(callback, param1, param2, pdMS_TO_TICKS(delayMsec));
} */
#ifdef ARCH_ESP32
// Super skanky quick hack to use hardware timers of the ESP32
static hw_timer_t *timer;
static PendableFunction tCallback;
static void *tParam1;
static uint32_t tParam2;
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) {
timer = timerBegin(0, 80, true); // one usec per tick (main clock is 80MhZ on ESP32)
assert(timer);
timerAttachInterrupt(timer, &onTimer, true);
}
tCallback = callback;
tParam1 = param1;
tParam2 = param2;
timerAlarmWrite(timer, delayMsec * 1000UL, false); // Do not reload, we want it to be a single shot timer
timerRestart(timer);
timerAlarmEnable(timer);
return true;
}
#endif

View File

@@ -1,8 +0,0 @@
#pragma once
#include <Arduino.h>
typedef void (*PendableFunction)(void *pvParameter1, uint32_t ulParameter2);
/// Uses a hardware timer, but calls the handler in _interrupt_ context
bool scheduleHWCallback(PendableFunction callback, void *param1, uint32_t param2, uint32_t delayMsec);

View File

@@ -80,9 +80,6 @@ RAK9154Sensor rak9154Sensor;
#endif
#ifdef HAS_PMU
#include "XPowersAXP192.tpp"
#include "XPowersAXP2101.tpp"
#include "XPowersLibInterface.hpp"
XPowersLibInterface *PMU = NULL;
#else
@@ -200,7 +197,8 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
#endif
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) && !defined(HAS_PMU) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !defined(HAS_PMU) && \
!MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
if (hasINA()) {
LOG_DEBUG("Using INA on I2C addr 0x%x for device battery voltage\n", config.power.device_battery_ina_address);
return getINAVoltage();
@@ -420,7 +418,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
}
#endif
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO)
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
uint16_t getINAVoltage()
{
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
@@ -460,7 +458,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
#endif
};
AnalogBatteryLevel analogLevel;
static AnalogBatteryLevel analogLevel;
Power::Power() : OSThread("Power")
{
@@ -559,6 +557,10 @@ bool Power::setup()
{
bool found = axpChipInit() || analogInit();
#ifdef NRF_APM
found = true;
#endif
enabled = found;
low_voltage_counter = 0;
@@ -588,10 +590,16 @@ void Power::shutdown()
// TODO(girts): move this and other axp stuff to power.h/power.cpp.
void Power::readPowerStatus()
{
int32_t batteryVoltageMv = -1; // Assume unknown
int8_t batteryChargePercent = -1;
OptionalBool usbPowered = OptUnknown;
OptionalBool hasBattery = OptUnknown; // These must be static because NRF_APM code doesn't run every time
OptionalBool isCharging = OptUnknown;
if (batteryLevel) {
bool hasBattery = batteryLevel->isBatteryConnect();
uint32_t batteryVoltageMv = 0;
int8_t batteryChargePercent = 0;
hasBattery = batteryLevel->isBatteryConnect() ? OptTrue : OptFalse;
usbPowered = batteryLevel->isVbusIn() ? OptTrue : OptFalse;
isCharging = batteryLevel->isCharging() ? OptTrue : OptFalse;
if (hasBattery) {
batteryVoltageMv = batteryLevel->getBattVoltage();
// If the AXP192 returns a valid battery percentage, use it
@@ -606,102 +614,90 @@ void Power::readPowerStatus()
0, 100);
}
}
}
OptionalBool NRF_USB = OptFalse;
// FIXME: IMO we shouldn't be littering our code with all these ifdefs. Way better instead to make a Nrf52IsUsbPowered subclass
// (which shares a superclass with the BatteryLevel stuff)
// that just provides a few methods. But in the interest of fixing this bug I'm going to follow current
// practice.
#ifdef NRF_APM // Section of code detects USB power on the RAK4631 and updates the power states. Takes 20 seconds or so to detect
// changes.
static nrfx_power_usb_state_t prev_nrf_usb_state = (nrfx_power_usb_state_t)-1; // -1 so that state detected at boot
nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get();
nrfx_power_usb_state_t nrf_usb_state = nrfx_power_usbstatus_get();
// LOG_DEBUG("NRF Power %d\n", nrf_usb_state);
// If state changed
if (nrf_usb_state != prev_nrf_usb_state) {
// If changed to DISCONNECTED
if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED) {
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
NRF_USB = OptFalse;
}
// If changed to CONNECTED / READY
else {
powerFSM.trigger(EVENT_POWER_CONNECTED);
NRF_USB = OptTrue;
}
// If changed to DISCONNECTED
if (nrf_usb_state == NRFX_POWER_USB_STATE_DISCONNECTED)
isCharging = usbPowered = OptFalse;
// If changed to CONNECTED / READY
else
isCharging = usbPowered = OptTrue;
// Cache the current state
prev_nrf_usb_state = nrf_usb_state;
}
#endif
// Notify any status instances that are observing us
const PowerStatus powerStatus2 = PowerStatus(
hasBattery ? OptTrue : OptFalse, batteryLevel->isVbusIn() || NRF_USB == OptTrue ? OptTrue : OptFalse,
batteryLevel->isCharging() || NRF_USB == OptTrue ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(),
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
newStatus.notifyObservers(&powerStatus2);
// Notify any status instances that are observing us
const PowerStatus powerStatus2 = PowerStatus(hasBattery, usbPowered, isCharging, batteryVoltageMv, batteryChargePercent);
LOG_DEBUG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus2.getHasUSB(),
powerStatus2.getIsCharging(), powerStatus2.getBatteryVoltageMv(), powerStatus2.getBatteryChargePercent());
newStatus.notifyObservers(&powerStatus2);
#ifdef DEBUG_HEAP
if (lastheap != memGet.getFreeHeap()) {
LOG_DEBUG("Threads running:");
int running = 0;
for (int i = 0; i < MAX_THREADS; i++) {
auto thread = concurrency::mainController.get(i);
if ((thread != nullptr) && (thread->enabled)) {
LOG_DEBUG(" %s", thread->ThreadName.c_str());
running++;
}
if (lastheap != memGet.getFreeHeap()) {
LOG_DEBUG("Threads running:");
int running = 0;
for (int i = 0; i < MAX_THREADS; i++) {
auto thread = concurrency::mainController.get(i);
if ((thread != nullptr) && (thread->enabled)) {
LOG_DEBUG(" %s", thread->ThreadName.c_str());
running++;
}
LOG_DEBUG("\n");
LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads\n", memGet.getFreeHeap(), memGet.getHeapSize(),
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
lastheap = memGet.getFreeHeap();
}
LOG_DEBUG("\n");
LOG_DEBUG("Heap status: %d/%d bytes free (%d), running %d/%d threads\n", memGet.getFreeHeap(), memGet.getHeapSize(),
memGet.getFreeHeap() - lastheap, running, concurrency::mainController.size(false));
lastheap = memGet.getFreeHeap();
}
#ifdef DEBUG_HEAP_MQTT
if (mqtt) {
// send MQTT-Packet with Heap-Size
uint8_t dmac[6];
getMacAddr(dmac); // Get our hardware ID
char mac[18];
sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]);
if (mqtt) {
// send MQTT-Packet with Heap-Size
uint8_t dmac[6];
getMacAddr(dmac); // Get our hardware ID
char mac[18];
sprintf(mac, "!%02x%02x%02x%02x", dmac[2], dmac[3], dmac[4], dmac[5]);
auto newHeap = memGet.getFreeHeap();
std::string heapTopic =
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/heap/") + std::string(mac);
std::string heapString = std::to_string(newHeap);
mqtt->pubSub.publish(heapTopic.c_str(), heapString.c_str(), false);
auto wifiRSSI = WiFi.RSSI();
std::string wifiTopic =
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/wifi/") + std::string(mac);
std::string wifiString = std::to_string(wifiRSSI);
mqtt->pubSub.publish(wifiTopic.c_str(), wifiString.c_str(), false);
}
auto newHeap = memGet.getFreeHeap();
std::string heapTopic =
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/heap/") + std::string(mac);
std::string heapString = std::to_string(newHeap);
mqtt->pubSub.publish(heapTopic.c_str(), heapString.c_str(), false);
auto wifiRSSI = WiFi.RSSI();
std::string wifiTopic =
(*moduleConfig.mqtt.root ? moduleConfig.mqtt.root : "msh") + std::string("/2/wifi/") + std::string(mac);
std::string wifiString = std::to_string(wifiRSSI);
mqtt->pubSub.publish(wifiTopic.c_str(), wifiString.c_str(), false);
}
#endif
#endif
// If we have a battery at all and it is less than 0%, force deep sleep if we have more than 10 low readings in
// a row. NOTE: min LiIon/LiPo voltage is 2.0 to 2.5V, current OCV min is set to 3100 that is large enough.
//
if (powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) {
if (batteryLevel->getBattVoltage() < OCV[NUM_OCV_POINTS - 1]) {
low_voltage_counter++;
LOG_DEBUG("Low voltage counter: %d/10\n", low_voltage_counter);
if (low_voltage_counter > 10) {
// If we have a battery at all and it is less than 0%, force deep sleep if we have more than 10 low readings in
// a row. NOTE: min LiIon/LiPo voltage is 2.0 to 2.5V, current OCV min is set to 3100 that is large enough.
//
if (batteryLevel && powerStatus2.getHasBattery() && !powerStatus2.getHasUSB()) {
if (batteryLevel->getBattVoltage() < OCV[NUM_OCV_POINTS - 1]) {
low_voltage_counter++;
LOG_DEBUG("Low voltage counter: %d/10\n", low_voltage_counter);
if (low_voltage_counter > 10) {
#ifdef ARCH_NRF52
// We can't trigger deep sleep on NRF52, it's freezing the board
LOG_DEBUG("Low voltage detected, but not triggering deep sleep\n");
// We can't trigger deep sleep on NRF52, it's freezing the board
LOG_DEBUG("Low voltage detected, but not triggering deep sleep\n");
#else
LOG_INFO("Low voltage detected, triggering deep sleep\n");
powerFSM.trigger(EVENT_LOW_BATTERY);
LOG_INFO("Low voltage detected, triggering deep sleep\n");
powerFSM.trigger(EVENT_LOW_BATTERY);
#endif
}
} else {
low_voltage_counter = 0;
}
} else {
low_voltage_counter = 0;
}
} else {
// No power sensing on this board - tell everyone else we have no idea what is happening
const PowerStatus powerStatus3 = PowerStatus(OptUnknown, OptUnknown, OptUnknown, -1, -1);
newStatus.notifyObservers(&powerStatus3);
}
}
@@ -1050,4 +1046,4 @@ bool Power::axpChipInit()
#else
return false;
#endif
}
}

View File

@@ -9,6 +9,7 @@
*/
#include "PowerFSM.h"
#include "Default.h"
#include "Led.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerMon.h"
@@ -21,12 +22,15 @@
#ifndef SLEEP_TIME
#define SLEEP_TIME 30
#endif
#if EXCLUDE_POWER_FSM
FakeFsm powerFSM;
void PowerFSM_setup(){};
#else
/// Should we behave as if we have AC power now?
static bool isPowered()
{
// Circumvent the battery sensing logic and assumes constant power if no battery pin or power mgmt IC
#if !defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101)
#if !defined(BATTERY_PIN) && !defined(HAS_AXP192) && !defined(HAS_AXP2101) && !defined(NRF_APM)
return true;
#endif
@@ -50,7 +54,6 @@ static bool isPowered()
static void sdsEnter()
{
LOG_DEBUG("Enter state: SDS\n");
powerMon->setState(meshtastic_PowerMon_State_CPU_DeepSleep);
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false);
}
@@ -70,7 +73,6 @@ static uint32_t secsSlept;
static void lsEnter()
{
LOG_INFO("lsEnter begin, ls_secs=%u\n", config.power.ls_secs);
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(false);
secsSlept = 0; // How long have we been sleeping this time
@@ -91,7 +93,7 @@ static void lsIdle()
uint32_t sleepTime = SLEEP_TIME;
powerMon->setState(meshtastic_PowerMon_State_CPU_LightSleep);
setLed(false); // Never leave led on while in light sleep
ledBlink.set(false); // Never leave led on while in light sleep
esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL);
powerMon->clearState(meshtastic_PowerMon_State_CPU_LightSleep);
@@ -99,7 +101,7 @@ static void lsIdle()
case ESP_SLEEP_WAKEUP_TIMER:
// Normal case: timer expired, we should just go back to sleep ASAP
setLed(true); // briefly turn on led
ledBlink.set(true); // briefly turn on led
wakeCause2 = doLightSleep(100); // leave led on for 1ms
secsSlept += sleepTime;
@@ -134,7 +136,7 @@ static void lsIdle()
}
} else {
// Time to stop sleeping!
setLed(false);
ledBlink.set(false);
LOG_INFO("Reached ls_secs, servicing loop()\n");
powerFSM.trigger(EVENT_WAKE_TIMER);
}
@@ -149,7 +151,6 @@ static void lsExit()
static void nbEnter()
{
LOG_DEBUG("Enter state: NB\n");
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
screen->setOn(false);
#ifdef ARCH_ESP32
// Only ESP32 should turn off bluetooth
@@ -161,8 +162,6 @@ static void nbEnter()
static void darkEnter()
{
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
setBluetoothEnable(true);
screen->setOn(false);
}
@@ -170,8 +169,6 @@ static void darkEnter()
static void serialEnter()
{
LOG_DEBUG("Enter state: SERIAL\n");
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
setBluetoothEnable(false);
screen->setOn(true);
screen->print("Serial connected\n");
@@ -180,7 +177,6 @@ static void serialEnter()
static void serialExit()
{
// Turn bluetooth back on when we leave serial stream API
powerMon->setState(meshtastic_PowerMon_State_BT_On);
setBluetoothEnable(true);
screen->print("Serial disconnected\n");
}
@@ -193,8 +189,6 @@ static void powerEnter()
LOG_INFO("Loss of power in Powered\n");
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
} else {
powerMon->setState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true);
setBluetoothEnable(true);
// within enter() the function getState() returns the state we came from
@@ -218,8 +212,6 @@ static void powerIdle()
static void powerExit()
{
powerMon->setState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true);
setBluetoothEnable(true);
@@ -231,8 +223,6 @@ static void powerExit()
static void onEnter()
{
LOG_DEBUG("Enter state: ON\n");
powerMon->setState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true);
setBluetoothEnable(true);
}
@@ -408,4 +398,5 @@ void PowerFSM_setup()
#endif
powerFSM.run_machine(); // run one iteration of the state machine, so we run our on enter tasks for the initial DARK state
}
}
#endif

View File

@@ -1,6 +1,6 @@
#pragma once
#include <Fsm.h>
#include "configuration.h"
// See sw-design.md for documentation
@@ -22,7 +22,30 @@
#define EVENT_SHUTDOWN 16 // force a full shutdown now (not just sleep)
#define EVENT_INPUT 17 // input broker wants something, we need to wake up and enable screen
#if EXCLUDE_POWER_FSM
class FakeFsm
{
public:
void trigger(int event)
{
if (event == EVENT_SERIAL_CONNECTED) {
serialConnected = true;
} else if (event == EVENT_SERIAL_DISCONNECTED) {
serialConnected = false;
}
};
bool getState() { return serialConnected; };
private:
bool serialConnected = false;
};
extern FakeFsm powerFSM;
void PowerFSM_setup();
#else
#include <Fsm.h>
extern Fsm powerFSM;
extern State stateON, statePOWER, stateSERIAL, stateDARK;
void PowerFSM_setup();
#endif

View File

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

View File

@@ -2,9 +2,11 @@
#include "NodeDB.h"
// Use the 'live' config flag to figure out if we should be showing this message
static bool is_power_enabled(uint64_t m)
bool PowerMon::is_power_enabled(uint64_t m)
{
return (m & config.power.powermon_enables) ? true : false;
// FIXME: VERY STRANGE BUG: if I or in "force_enabled || " the flashed image on a rak4631 is not accepted by the bootloader as
// valid!!! Possibly a linker/gcc/bootloader bug somewhere?
return ((m & config.power.powermon_enables) ? true : false);
}
void PowerMon::setState(_meshtastic_PowerMon_State state, const char *reason)

View File

@@ -17,6 +17,13 @@ class PowerMon
{
uint64_t states = 0UL;
friend class PowerStressModule;
/**
* If stress testing we always want all events logged
*/
bool force_enabled = false;
public:
PowerMon() {}
@@ -27,6 +34,9 @@ class PowerMon
private:
// Emit the coded log message
void emitLog(const char *reason);
// Use the 'live' config flag to figure out if we should be showing this message
bool is_power_enabled(uint64_t m);
};
extern PowerMon *powerMon;

View File

@@ -49,7 +49,17 @@ size_t RedirectablePrint::write(uint8_t c)
size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_list arg)
{
va_list copy;
#if ENABLE_JSON_LOGGING || ARCH_PORTDUINO
static char printBuf[512];
#else
static char printBuf[160];
#endif
#ifdef ARCH_PORTDUINO
bool color = !settingsMap[ascii_logs];
#else
bool color = true;
#endif
va_copy(copy, arg);
size_t len = vsnprintf(printBuf, sizeof(printBuf), format, copy);
@@ -66,7 +76,7 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l
if (!std::isprint(static_cast<unsigned char>(printBuf[f])) && printBuf[f] != '\n')
printBuf[f] = '#';
}
if (logLevel != nullptr) {
if (color && logLevel != nullptr) {
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
Print::write("\u001b[34m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
@@ -77,7 +87,9 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l
Print::write("\u001b[31m", 6);
}
len = Print::write(printBuf, len);
Print::write("\u001b[0m", 5);
if (color && logLevel != nullptr) {
Print::write("\u001b[0m", 5);
}
return len;
}
@@ -87,17 +99,27 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format,
// Cope with 0 len format strings, but look for new line terminator
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
#ifdef ARCH_PORTDUINO
bool color = !settingsMap[ascii_logs];
#else
bool color = true;
#endif
// If we are the first message on a report, include the header
if (!isContinuationMessage) {
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
Print::write("\u001b[34m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
Print::write("\u001b[32m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
Print::write("\u001b[33m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0)
Print::write("\u001b[31m", 6);
if (color) {
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
Print::write("\u001b[34m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
Print::write("\u001b[32m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
Print::write("\u001b[33m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0)
Print::write("\u001b[31m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0)
Print::write("\u001b[35m", 6);
}
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
@@ -111,17 +133,33 @@ void RedirectablePrint::log_to_serial(const char *logLevel, const char *format,
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
#ifdef ARCH_PORTDUINO
::printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
::printf("%s ", logLevel);
if (color) {
::printf("\u001b[0m");
}
::printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000);
#else
printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
printf("%s ", logLevel);
if (color) {
printf("\u001b[0m");
}
printf("| %02d:%02d:%02d %u ", hour, min, sec, millis() / 1000);
#endif
} else
} else {
#ifdef ARCH_PORTDUINO
::printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000);
::printf("%s ", logLevel);
if (color) {
::printf("\u001b[0m");
}
::printf("| ??:??:?? %u ", millis() / 1000);
#else
printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000);
printf("%s ", logLevel);
if (color) {
printf("\u001b[0m");
}
printf("| ??:??:?? %u ", millis() / 1000);
#endif
}
auto thread = concurrency::OSThread::currentThread;
if (thread) {
print("[");
@@ -244,7 +282,21 @@ meshtastic_LogRecord_Level RedirectablePrint::getLogLevel(const char *logLevel)
void RedirectablePrint::log(const char *logLevel, const char *format, ...)
{
#ifdef ARCH_PORTDUINO
#if ARCH_PORTDUINO
// level trace is special, two possible ways to handle it.
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0) {
if (settingsStrings[traceFilename] != "") {
va_list arg;
va_start(arg, format);
try {
traceFile << va_arg(arg, char *) << std::endl;
} catch (const std::ios_base::failure &e) {
}
va_end(arg);
}
if (settingsMap[logoutputlevel] < level_trace && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_TRACE) == 0)
return;
}
if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
return;
else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
@@ -330,4 +382,4 @@ std::string RedirectablePrint::mt_sprintf(const std::string fmt_str, ...)
break;
}
return std::string(formatted.get());
}
}

105
src/SafeFile.cpp Normal file
View File

@@ -0,0 +1,105 @@
#include "SafeFile.h"
#ifdef FSCom
// Only way to work on both esp32 and nrf52
static File openFile(const char *filename, bool fullAtomic)
{
if (!fullAtomic)
FSCom.remove(filename); // Nuke the old file to make space (ignore if it !exists)
String filenameTmp = filename;
filenameTmp += ".tmp";
// clear any previous LFS errors
lfs_assert_failed = false;
return FSCom.open(filenameTmp.c_str(), FILE_O_WRITE);
}
SafeFile::SafeFile(const char *_filename, bool fullAtomic)
: filename(_filename), f(openFile(_filename, fullAtomic)), fullAtomic(fullAtomic)
{
}
size_t SafeFile::write(uint8_t ch)
{
if (!f)
return 0;
hash ^= ch;
return f.write(ch);
}
size_t SafeFile::write(const uint8_t *buffer, size_t size)
{
if (!f)
return 0;
for (size_t i = 0; i < size; i++) {
hash ^= buffer[i];
}
return f.write((uint8_t const *)buffer, size); // This nasty cast is _IMPORTANT_ otherwise the correct adafruit method does
// not get used (they made a mistake in their typing)
}
/**
* Atomically close the file (deleting any old versions) and readback the contents to confirm the hash matches
*
* @return false for failure
*/
bool SafeFile::close()
{
if (!f)
return false;
f.close();
if (!testReadback())
return false;
// brief window of risk here ;-)
if (fullAtomic && FSCom.exists(filename.c_str()) && !FSCom.remove(filename.c_str())) {
LOG_ERROR("Can't remove old pref file\n");
return false;
}
String filenameTmp = filename;
filenameTmp += ".tmp";
if (!renameFile(filenameTmp.c_str(), filename.c_str())) {
LOG_ERROR("Error: can't rename new pref file\n");
return false;
}
return true;
}
/// Read our (closed) tempfile back in and compare the hash
bool SafeFile::testReadback()
{
bool lfs_failed = lfs_assert_failed;
lfs_assert_failed = false;
String filenameTmp = filename;
filenameTmp += ".tmp";
auto f2 = FSCom.open(filenameTmp.c_str(), FILE_O_READ);
if (!f2) {
LOG_ERROR("Can't open tmp file for readback\n");
return false;
}
int c = 0;
uint8_t test_hash = 0;
while ((c = f2.read()) >= 0) {
test_hash ^= (uint8_t)c;
}
f2.close();
if (test_hash != hash) {
LOG_ERROR("Readback failed hash mismatch\n");
return false;
}
return !lfs_failed;
}
#endif

49
src/SafeFile.h Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
#include "FSCommon.h"
#include "configuration.h"
#ifdef FSCom
/**
* This class provides 'safe'/paranoid file writing.
*
* Some of our filesystems (in particular the nrf52) may have bugs beneath our layer. Therefore we want to
* be very careful about how we write files. This class provides a restricted (Stream only) writing API for writing to files.
*
* Notably:
* - we keep a simple xor hash of all characters that were written.
* - We do not allow seeking (because we want to maintain our hash)
* - we provide an close() method which is similar to close but returns false if we were unable to successfully write the
* file. Also this method
* - atomically replaces any old version of the file on the disk with our new file (after first rereading the file from the disk
* to confirm the hash matches)
* - Some files are super huge so we can't do the full atomic rename/copy (because of filesystem size limits). If !fullAtomic
* then we still do the readback to verify file is valid so higher level code can handle failures.
*/
class SafeFile : public Print
{
public:
SafeFile(char const *filepath, bool fullAtomic = false);
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buffer, size_t size);
/**
* Atomically close the file (deleting any old versions) and readback the contents to confirm the hash matches
*
* @return false for failure
*/
bool close();
private:
/// Read our (closed) tempfile back in and compare the hash
bool testReadback();
String filename;
File f;
bool fullAtomic;
uint8_t hash = 0;
};
#endif

View File

@@ -96,7 +96,7 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg)
{
if (usingProtobufs) {
if (usingProtobufs && config.device.debug_log_enabled) {
meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset
switch (logLevel[0]) {
case 'D':
@@ -120,4 +120,4 @@ void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_l
emitLogRecord(ll, thread ? thread->ThreadName.c_str() : "", format, arg);
} else
RedirectablePrint::log_to_serial(logLevel, format, arg);
}
}

View File

View File

@@ -52,10 +52,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// Configuration
// -----------------------------------------------------------------------------
// If we are using the JTAG port for debugging, some pins must be left free for that (and things like GPS have to be disabled)
// we don't support jtag on the ttgo - access to gpio 12 is a PITA
#define REQUIRE_RADIO true // If true, we will fail to start if the radio is not found
/// Convert a preprocessor name into a quoted string
#define xstr(s) ystr(s)
#define ystr(s) #s
@@ -259,6 +255,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MESHTASTIC_EXCLUDE_SCREEN 1
#define MESHTASTIC_EXCLUDE_MQTT 1
#define MESHTASTIC_EXCLUDE_POWERMON 1
#define MESHTASTIC_EXCLUDE_I2C 1
#define MESHTASTIC_EXCLUDE_POWER_FSM 1
#define MESHTASTIC_EXCLUDE_TZ 1
#endif
// Turn off all optional modules
@@ -272,6 +271,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MESHTASTIC_EXCLUDE_RANGETEST 1
#define MESHTASTIC_EXCLUDE_REMOTEHARDWARE 1
#define MESHTASTIC_EXCLUDE_STOREFORWARD 1
#define MESHTASTIC_EXCLUDE_TEXTMESSAGE 1
#define MESHTASTIC_EXCLUDE_ATAK 1
#define MESHTASTIC_EXCLUDE_CANNEDMESSAGES 1
#define MESHTASTIC_EXCLUDE_NEIGHBORINFO 1
@@ -313,4 +313,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#endif
#include "DebugConfiguration.h"
#include "RF95Configuration.h"
#include "RF95Configuration.h"

View File

@@ -1,7 +1,8 @@
#include "ScanI2CTwoWire.h"
#if !MESHTASTIC_EXCLUDE_I2C
#include "concurrency/LockGuard.h"
#include "configuration.h"
#if defined(ARCH_PORTDUINO)
#include "linux/LinuxHardwareI2C.h"
#endif
@@ -403,3 +404,4 @@ size_t ScanI2CTwoWire::countDevices() const
{
return foundDevices.size();
}
#endif

View File

@@ -1,5 +1,8 @@
#pragma once
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_I2C
#include <map>
#include <memory>
#include <stddef.h>
@@ -55,4 +58,5 @@ class ScanI2CTwoWire : public ScanI2C
uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth) const;
DeviceType probeOLED(ScanI2C::DeviceAddress) const;
};
};
#endif

View File

@@ -12,7 +12,7 @@
#include <freertos/task.h>
#endif
#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_STM32) || defined(ARDUINO_ARCH_RP2040)
#if defined(ARDUINO_NRF52_ADAFRUIT) || defined(ARDUINO_ARCH_RP2040)
#define HAS_FREE_RTOS
#include <FreeRTOS.h>

View File

@@ -400,7 +400,6 @@ bool GPS::setup()
int msglen = 0;
if (!didSerialInit) {
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
// if GPS_BAUDRATE is specified in variant (i.e. not 9600), skip to the specified rate.
@@ -505,6 +504,22 @@ bool GPS::setup()
delay(250);
_serial_gps->write("$CFGMSG,6,1,0\r\n");
delay(250);
} else if (gnssModel == GNSS_MODEL_AG3335) {
_serial_gps->write("$PAIR066,1,0,1,0,0,1*3B"); // Enable GPS+GALILEO+NAVIC
// Configure NMEA (sentences will output once per fix)
_serial_gps->write("$PAIR062,0,0*3F"); // GGA ON
_serial_gps->write("$PAIR062,1,0*3F"); // GLL OFF
_serial_gps->write("$PAIR062,2,1*3D"); // GSA ON
_serial_gps->write("$PAIR062,3,0*3D"); // GSV OFF
_serial_gps->write("$PAIR062,4,0*3B"); // RMC ON
_serial_gps->write("$PAIR062,5,0*3B"); // VTG OFF
_serial_gps->write("$PAIR062,6,1*39"); // ZDA ON
delay(250);
_serial_gps->write("$PAIR513*3D"); // save configuration
} else if (gnssModel == GNSS_MODEL_UBLOX) {
// 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
@@ -787,7 +802,6 @@ GPS::~GPS()
// we really should unregister our sleep observer
notifyDeepSleepObserver.unobserve(&notifyDeepSleep);
}
// Put the GPS hardware into a specified state
void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
{
@@ -796,6 +810,13 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
powerState = newState;
LOG_INFO("GPS power state moving from %s to %s\n", getGPSPowerStateString(oldState), getGPSPowerStateString(newState));
#ifdef HELTEC_MESH_NODE_T114
if ((oldState == GPS_OFF || oldState == GPS_HARDSLEEP) && (newState != GPS_OFF && newState != GPS_HARDSLEEP)) {
_serial_gps->begin(serialSpeeds[speedSelect]);
} else if ((newState == GPS_OFF || newState == GPS_HARDSLEEP) && (oldState != GPS_OFF && oldState != GPS_HARDSLEEP)) {
_serial_gps->end();
}
#endif
switch (newState) {
case GPS_ACTIVE:
case GPS_IDLE:
@@ -824,6 +845,11 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
setPowerPMU(false); // Power (PMU): off
writePinStandby(true); // Standby (pin): asleep (not awake)
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
#ifdef GNSS_AIROHA
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
digitalWrite(PIN_GPS_EN, LOW);
}
#endif
break;
case GPS_OFF:
@@ -833,6 +859,11 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
setPowerPMU(false); // Power (PMU): off
writePinStandby(true); // Standby (pin): asleep
setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely
#ifdef GNSS_AIROHA
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
digitalWrite(PIN_GPS_EN, LOW);
}
#endif
break;
}
}
@@ -1082,7 +1113,7 @@ int32_t GPS::runOnce()
if (devicestate.did_gps_reset && scheduling.elapsedSearchMs() > 60 * 1000UL && !hasFlow()) {
LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n");
devicestate.did_gps_reset = false;
nodeDB->saveDeviceStateToDisk();
nodeDB->saveToDisk(SEGMENT_DEVICESTATE);
return disable(); // Stop the GPS thread as it can do nothing useful until next reboot.
}
}
@@ -1162,7 +1193,7 @@ int GPS::prepareDeepSleep(void *unused)
GnssModel_t GPS::probe(int serialSpeed)
{
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040)
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040) || defined(ARCH_STM32WL)
_serial_gps->end();
_serial_gps->begin(serialSpeed);
#else
@@ -1171,9 +1202,9 @@ GnssModel_t GPS::probe(int serialSpeed)
_serial_gps->updateBaudRate(serialSpeed);
}
#endif
#ifdef GNSS_Airoha // add by WayenWeng
return GNSS_MODEL_UNKNOWN;
#else
#ifdef GNSS_AIROHA
return GNSS_MODEL_AG3335;
#endif
#ifdef GPS_DEBUG
for (int i = 0; i < 20; i++) {
getACK("$GP", 200);
@@ -1196,7 +1227,15 @@ GnssModel_t GPS::probe(int serialSpeed)
return GNSS_MODEL_UC6580;
}
// Get version information
clearBuffer();
_serial_gps->write("$PDTINFO\r\n");
delay(750);
if (getACK("UM600", 500) == GNSS_RESPONSE_OK) {
LOG_INFO("UM600 detected, using UC6580 Module\n");
return GNSS_MODEL_UC6580;
}
// Get version information for ATGM336H
clearBuffer();
_serial_gps->write("$PCAS06,1*1A\r\n");
if (getACK("$GPTXT,01,01,02,HW=ATGM336H", 500) == GNSS_RESPONSE_OK) {
@@ -1204,6 +1243,26 @@ GnssModel_t GPS::probe(int serialSpeed)
return GNSS_MODEL_ATGM336H;
}
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS))
based on AT6558 */
clearBuffer();
_serial_gps->write("$PCAS06,1*1A\r\n");
if (getACK("$GPTXT,01,01,02,HW=ATGM332D", 500) == GNSS_RESPONSE_OK) {
LOG_INFO("ATGM332D detected, using ATGM336H Module\n");
return GNSS_MODEL_ATGM336H;
}
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
clearBuffer();
_serial_gps->write("PAIR020*38\r\n");
if (getACK("$PAIR020,AG3335", 500) == GNSS_RESPONSE_OK) {
LOG_INFO("Aioha AG3335 detected, using AG3335 Module\n");
return GNSS_MODEL_AG3335;
}
// Get version information for Airoha AG3335
clearBuffer();
_serial_gps->write("$PMTK605*31\r\n");
// Get version information
clearBuffer();
_serial_gps->write("$PCAS06,0*1B\r\n");
@@ -1249,7 +1308,7 @@ GnssModel_t GPS::probe(int serialSpeed)
_serial_gps->write(_message_prt, sizeof(_message_prt));
delay(500);
serialSpeed = 9600;
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040)
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040) || defined(ARCH_STM32WL)
_serial_gps->end();
_serial_gps->begin(serialSpeed);
#else
@@ -1329,7 +1388,6 @@ GnssModel_t GPS::probe(int serialSpeed)
}
return GNSS_MODEL_UBLOX;
#endif // !GNSS_Airoha
}
GPS *GPS::createGps()
@@ -1484,11 +1542,25 @@ bool GPS::factoryReset()
*/
bool GPS::lookForTime()
{
#ifdef GNSS_Airoha // add by WayenWeng
#ifdef GNSS_AIROHA
uint8_t fix = reader.fixQuality();
uint32_t now = millis();
if (fix > 0) {
if (lastFixStartMsec > 0) {
if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) {
return false;
} else {
clearBuffer();
}
} else {
lastFixStartMsec = now;
return false;
}
} else {
return false;
}
#endif
auto ti = reader.time;
auto d = reader.date;
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
@@ -1523,13 +1595,26 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
*/
bool GPS::lookForLocation()
{
#ifdef GNSS_Airoha // add by WayenWeng
#ifdef GNSS_AIROHA
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
uint8_t fix = reader.fixQuality();
uint32_t now = millis();
if (fix > 0) {
if (lastFixStartMsec > 0) {
if ((now - lastFixStartMsec) < GPS_FIX_HOLD_TIME) {
return false;
} else {
clearBuffer();
}
} else {
lastFixStartMsec = now;
return false;
}
} else {
return false;
}
}
#endif
// By default, TinyGPS++ does not parse GPGSA lines, which give us
// the 2D/3D fixType (see NMEAGPS.h)
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
@@ -1739,6 +1824,12 @@ void GPS::toggleGpsMode()
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED;
LOG_INFO("User toggled GpsMode. Now DISABLED.\n");
#ifdef GNSS_AIROHA
if (powerState == GPS_ACTIVE) {
LOG_DEBUG("User power Off GPS\n");
digitalWrite(PIN_GPS_EN, LOW);
}
#endif
disable();
} else if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_DISABLED) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;

View File

@@ -28,7 +28,8 @@ typedef enum {
GNSS_MODEL_UBLOX,
GNSS_MODEL_UC6580,
GNSS_MODEL_UNKNOWN,
GNSS_MODEL_MTK_L76B
GNSS_MODEL_MTK_L76B,
GNSS_MODEL_AG3335
} GnssModel_t;
typedef enum {
@@ -50,7 +51,7 @@ enum GPSPowerState : uint8_t {
const char *getDOPString(uint32_t dop);
/**
* A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading)
* A gps class that only reads from the GPS periodically and keeps the gps powered down except when reading
*
* When new data is available it will notify observers.
*/
@@ -69,7 +70,7 @@ class GPS : private concurrency::OSThread
#endif
private:
const int serialSpeeds[6] = {9600, 4800, 38400, 57600, 115200, 9600};
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
uint32_t rx_gpio = 0;
uint32_t tx_gpio = 0;
uint32_t en_gpio = 0;
@@ -302,4 +303,4 @@ class GPS : private concurrency::OSThread
};
extern GPS *gps;
#endif // Exclude GPS
#endif // Exclude GPS

View File

@@ -493,7 +493,7 @@ std::shared_ptr<GeoCoord> GeoCoord::pointAtDistance(double bearing, double range
* The bearing in string format
* @return Bearing in degrees
*/
uint GeoCoord::bearingToDegrees(const char *bearing)
unsigned int GeoCoord::bearingToDegrees(const char *bearing)
{
if (strcmp(bearing, "N") == 0)
return 0;
@@ -537,7 +537,7 @@ uint GeoCoord::bearingToDegrees(const char *bearing)
* The bearing in degrees
* @return Bearing in string format
*/
const char *GeoCoord::degreesToBearing(uint degrees)
const char *GeoCoord::degreesToBearing(unsigned int degrees)
{
if (degrees >= 348 || degrees < 11)
return "N";

View File

@@ -117,8 +117,8 @@ class GeoCoord
static float bearing(double lat1, double lon1, double lat2, double lon2);
static float rangeRadiansToMeters(double range_radians);
static float rangeMetersToRadians(double range_meters);
static uint bearingToDegrees(const char *bearing);
static const char *degreesToBearing(uint degrees);
static unsigned int bearingToDegrees(const char *bearing);
static const char *degreesToBearing(unsigned int degrees);
// Point to point conversions
int32_t distanceTo(const GeoCoord &pointB);

View File

@@ -6,6 +6,7 @@
#include <time.h>
static RTCQuality currentQuality = RTCQualityNone;
uint32_t lastSetFromPhoneNtpOrGps = 0;
RTCQuality getRTCQuality()
{
@@ -121,6 +122,9 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate)
if (shouldSet) {
currentQuality = q;
lastSetMsec = now;
if (currentQuality >= RTCQualityNTP) {
lastSetFromPhoneNtpOrGps = now;
}
// This delta value works on all platforms
timeStartMsec = now;
@@ -256,6 +260,7 @@ uint32_t getValidTime(RTCQuality minQuality, bool local)
time_t gm_mktime(struct tm *tm)
{
#if !MESHTASTIC_EXCLUDE_TZ
setenv("TZ", "GMT0", 1);
time_t res = mktime(tm);
if (*config.device.tzdef) {
@@ -264,4 +269,7 @@ time_t gm_mktime(struct tm *tm)
setenv("TZ", "UTC0", 1);
}
return res;
#else
return mktime(tm);
#endif
}

View File

@@ -24,6 +24,8 @@ enum RTCQuality {
RTCQuality getRTCQuality();
extern uint32_t lastSetFromPhoneNtpOrGps;
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv, bool forceUpdate = false);
bool perhapsSetRTC(RTCQuality q, struct tm &t);
@@ -43,4 +45,4 @@ time_t gm_mktime(struct tm *tm);
#define SEC_PER_DAY 86400
#define SEC_PER_HOUR 3600
#define SEC_PER_MIN 60
#define SEC_PER_MIN 60

View File

@@ -174,13 +174,13 @@ bool EInkDisplay::connect()
adafruitDisplay->init();
adafruitDisplay->setRotation(3);
}
#elif defined(PCA10059)
#elif defined(PCA10059) || defined(ME25LS01)
{
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
adafruitDisplay->setRotation(3);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->init(115200, true, 40, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
adafruitDisplay->setRotation(0);
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
}
#elif defined(M5_COREINK)
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);

View File

@@ -375,7 +375,7 @@ void EInkDynamicDisplay::hashImage()
// Sum all bytes of the image buffer together
for (uint16_t b = 0; b < (displayWidth / 8) * displayHeight; b++) {
imageHash += buffer[b];
imageHash ^= buffer[b] << b;
}
}

View File

@@ -20,6 +20,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Screen.h"
#include "../userPrefs.h"
#include "PowerMon.h"
#include "configuration.h"
#if HAS_SCREEN
#include <OLEDDisplay.h>
@@ -35,6 +37,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "gps/RTC.h"
#include "graphics/ScreenFonts.h"
#include "graphics/images.h"
#include "input/ScanAndSelect.h"
#include "input/TouchScreenImpl1.h"
#include "main.h"
#include "mesh-pb-constants.h"
@@ -156,7 +159,11 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl
display->setFont(FONT_MEDIUM);
display->setTextAlignment(TEXT_ALIGN_LEFT);
#ifdef SPLASH_TITLE_USERPREFS
const char *title = SPLASH_TITLE_USERPREFS;
#else
const char *title = "meshtastic.org";
#endif
display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
display->setFont(FONT_SMALL);
@@ -1569,6 +1576,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
if (on != screenOn) {
if (on) {
LOG_INFO("Turning on screen\n");
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
#ifdef T_WATCH_S3
PMU->enablePowerOutput(XPOWERS_ALDO2);
#endif
@@ -1583,6 +1591,9 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
dispdev->displayOn();
#ifdef USE_ST7789
pinMode(VTFT_CTRL, OUTPUT);
digitalWrite(VTFT_CTRL, LOW);
ui->init();
#ifdef ESP_PLATFORM
analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT);
#else
@@ -1594,16 +1605,28 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
setInterval(0); // Draw ASAP
runASAP = true;
} else {
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
#ifdef USE_EINK
// eInkScreensaver parameter is usually NULL (default argument), default frame used instead
setScreensaverFrames(einkScreensaver);
#endif
LOG_INFO("Turning off screen\n");
dispdev->displayOff();
#ifdef USE_ST7789
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, !TFT_BACKLIGHT_ON);
SPI1.end();
#if defined(ARCH_ESP32)
pinMode(VTFT_LEDA, ANALOG);
pinMode(VTFT_CTRL, ANALOG);
pinMode(ST7789_RESET, ANALOG);
pinMode(ST7789_RS, ANALOG);
pinMode(ST7789_NSS, ANALOG);
#else
nrf_gpio_cfg_default(VTFT_LEDA);
nrf_gpio_cfg_default(VTFT_CTRL);
nrf_gpio_cfg_default(ST7789_RESET);
nrf_gpio_cfg_default(ST7789_RS);
nrf_gpio_cfg_default(ST7789_NSS);
#endif
#endif
#ifdef T_WATCH_S3
@@ -1897,6 +1920,13 @@ int32_t Screen::runOnce()
// standard screen loop handling here
if (config.display.auto_screen_carousel_secs > 0 &&
(millis() - lastScreenTransition) > (config.display.auto_screen_carousel_secs * 1000)) {
// If an E-Ink display struggles with fast refresh, force carousel to use full refresh instead
// Carousel is potentially a major source of E-Ink display wear
#if !defined(EINK_BACKGROUND_USES_FAST)
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC);
#endif
LOG_DEBUG("LastScreenTransition exceeded %ums transitioning to next frame\n", (millis() - lastScreenTransition));
handleOnPress();
}
@@ -2276,6 +2306,11 @@ void Screen::handlePrint(const char *text)
void Screen::handleOnPress()
{
// If Canned Messages is using the "Scan and Select" input, dismiss the canned message frame when user button is pressed
// Minimize impact as a courtesy, as "scan and select" may be used as default config for some boards
if (scanAndSelectInput != nullptr && scanAndSelectInput->dismissCannedMessageFrame())
return;
// 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) {

View File

@@ -1,3 +1,4 @@
#ifndef HAS_USERPREFS_SPLASH
#define icon_width 50
#define icon_height 28
static uint8_t icon_bits[] = {
@@ -17,4 +18,5 @@ static uint8_t icon_bits[] = {
0xFE, 0x00, 0x00, 0xFC, 0x01, 0x7E, 0x00, 0x7F, 0x00, 0x00, 0xF8, 0x01,
0x7E, 0x00, 0x3E, 0x00, 0x00, 0xF8, 0x01, 0x38, 0x00, 0x3C, 0x00, 0x00,
0x70, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, };
0x00, 0x00, 0x00, 0x00, };
#endif

View File

@@ -0,0 +1,260 @@
#include "ExpressLRSFiveWay.h"
#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE
static const char inputSourceName[] = "ExpressLRS5Way"; // should match "allow input source" string
/**
* @brief Calculate fuzz: half the distance to the next nearest neighbor for each joystick position.
*
* The goal is to avoid collisions between joystick positions while still maintaining
* the widest tolerance for the analog value.
*
* Example: {10,50,800,1000,300,1600}
* If we just choose the minimum difference for this array the value would
* be 40/2 = 20.
*
* 20 does not leave enough room for the joystick position using 1600 which
* could have a +-100 offset.
*
* Example Fuzz values: {20, 20, 100, 100, 125, 300} now the fuzz for the 1600
* position is 300 instead of 20
*/
void ExpressLRSFiveWay::calcFuzzValues()
{
for (unsigned int i = 0; i < N_JOY_ADC_VALUES; i++) {
uint16_t closestDist = 0xffff;
uint16_t ival = joyAdcValues[i];
// Find the closest value to ival
for (unsigned int j = 0; j < N_JOY_ADC_VALUES; j++) {
// Don't compare value with itself
if (j == i)
continue;
uint16_t jval = joyAdcValues[j];
if (jval < ival && (ival - jval < closestDist))
closestDist = ival - jval;
if (jval > ival && (jval - ival < closestDist))
closestDist = jval - ival;
} // for j
// And the fuzz is half the distance to the closest value
fuzzValues[i] = closestDist / 2;
// DBG("joy%u=%u f=%u, ", i, ival, fuzzValues[i]);
} // for i
}
int ExpressLRSFiveWay::readKey()
{
uint16_t value = analogRead(PIN_JOYSTICK);
constexpr uint8_t IDX_TO_INPUT[N_JOY_ADC_VALUES - 1] = {UP, DOWN, LEFT, RIGHT, OK};
for (unsigned int i = 0; i < N_JOY_ADC_VALUES - 1; ++i) {
if (value < (joyAdcValues[i] + fuzzValues[i]) && value > (joyAdcValues[i] - fuzzValues[i]))
return IDX_TO_INPUT[i];
}
return NO_PRESS;
}
ExpressLRSFiveWay::ExpressLRSFiveWay() : concurrency::OSThread(inputSourceName)
{
// ExpressLRS: init values
isLongPressed = false;
keyInProcess = NO_PRESS;
keyDownStart = 0;
// Express LRS: calculate the threshold for interpreting ADC values as various buttons
calcFuzzValues();
// Meshtastic: register with canned messages
inputBroker->registerSource(this);
}
// ExpressLRS: interpret reading as key events
void ExpressLRSFiveWay::update(int *keyValue, bool *keyLongPressed)
{
*keyValue = NO_PRESS;
int newKey = readKey();
uint32_t now = millis();
if (keyInProcess == NO_PRESS) {
// New key down
if (newKey != NO_PRESS) {
keyDownStart = now;
// DBGLN("down=%u", newKey);
}
} else {
// if key released
if (newKey == NO_PRESS) {
// DBGLN("up=%u", keyInProcess);
if (!isLongPressed) {
if ((now - keyDownStart) > KEY_DEBOUNCE_MS) {
*keyValue = keyInProcess;
*keyLongPressed = false;
}
}
isLongPressed = false;
}
// else if the key has changed while down, reset state for next go-around
else if (newKey != keyInProcess) {
newKey = NO_PRESS;
}
// else still pressing, waiting for long if not already signaled
else if (!isLongPressed) {
if ((now - keyDownStart) > KEY_LONG_PRESS_MS) {
*keyValue = keyInProcess;
*keyLongPressed = true;
isLongPressed = true;
}
}
} // if keyInProcess != NO_PRESS
keyInProcess = newKey;
}
// Meshtastic: runs at regular intervals
int32_t ExpressLRSFiveWay::runOnce()
{
uint32_t now = millis();
// Dismiss any alert frames after 2 seconds
// Feedback for GPS toggle / adhoc ping
if (alerting && now > alertingSinceMs + 2000) {
alerting = false;
screen->endAlert();
}
// Get key events from ExpressLRS code
int keyValue;
bool longPressed;
update(&keyValue, &longPressed);
// Do something about this key press
determineAction((KeyType)keyValue, longPressed ? LONG : SHORT);
// If there has been recent key activity, poll the joystick slightly more frequently
if (now < keyDownStart + (20 * 1000UL)) // Within last 20 seconds
return 100;
// Otherwise, poll slightly less often
// Too many missed pressed if much slower than 250ms
return 250;
}
// Determine what action to take when a button press is detected
// Written verbose for easier remapping by user
void ExpressLRSFiveWay::determineAction(KeyType key, PressLength length)
{
switch (key) {
case LEFT:
if (inCannedMessageMenu()) // If in canned message menu
sendKey(CANCEL); // exit the menu (press imaginary cancel key)
else
sendKey(LEFT);
break;
case RIGHT:
if (inCannedMessageMenu()) // If in canned message menu:
sendKey(CANCEL); // exit the menu (press imaginary cancel key)
else
sendKey(RIGHT);
break;
case UP:
if (length == LONG)
toggleGPS();
else
sendKey(UP);
break;
case DOWN:
if (length == LONG)
sendAdhocPing();
else
sendKey(DOWN);
break;
case OK:
if (length == LONG)
shutdown();
else
click(); // Use instead of sendKey(OK). Works better when canned message module disabled
break;
default:
break;
}
}
// Feed input to the canned messages module
void ExpressLRSFiveWay::sendKey(KeyType key)
{
InputEvent e;
e.source = inputSourceName;
e.inputEvent = key;
notifyObservers(&e);
}
// Enable or Disable a connected GPS
// Contained as one method for easier remapping of buttons by user
void ExpressLRSFiveWay::toggleGPS()
{
#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS
if (!config.device.disable_triple_click && (gps != nullptr)) {
gps->toggleGpsMode();
screen->startAlert("GPS Toggled");
alerting = true;
alertingSinceMs = millis();
}
#endif
}
// Send either node-info or position, on demand
// Contained as one method for easier remapping of buttons by user
void ExpressLRSFiveWay::sendAdhocPing()
{
service->refreshLocalMeshNode();
bool sentPosition = service->trySendPosition(NODENUM_BROADCAST, true);
// Show custom alert frame, with multi-line centering
screen->startAlert([sentPosition](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
uint16_t x_offset = display->width() / 2;
uint16_t y_offset = 26; // Same constant as the default startAlert frame
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, y_offset + y, "Sent ad-hoc");
display->drawString(x_offset + x, y_offset + FONT_HEIGHT_MEDIUM + y, sentPosition ? "position" : "nodeinfo");
});
alerting = true;
alertingSinceMs = millis();
}
// Shutdown the node (enter deep-sleep)
// Contained as one method for easier remapping of buttons by user
void ExpressLRSFiveWay::shutdown()
{
LOG_INFO("Shutdown from long press\n");
powerFSM.trigger(EVENT_PRESS);
screen->startAlert("Shutting down...");
// Don't set alerting = true. We don't want to auto-dismiss this alert.
playShutdownMelody(); // In case user adds a buzzer
shutdownAtMsec = millis() + 3000;
}
// Emulate user button, or canned message SELECT
// This is necessary as canned message module doesn't translate SELECT to user button presses if the module is disabled
// Contained as one method for easier remapping of buttons by user
void ExpressLRSFiveWay::click()
{
if (!moduleConfig.canned_message.enabled)
powerFSM.trigger(EVENT_PRESS);
else
sendKey(OK);
}
ExpressLRSFiveWay *expressLRSFiveWayInput = nullptr;
#endif

View File

@@ -0,0 +1,85 @@
/*
Input source for Radio Master Bandit Nano, and similar hardware.
Devices have a 5-button "resistor ladder" style joystick, read by ADC.
These devices do not use the ADC to monitor input voltage.
Much of this code taken directly from ExpressLRS FiveWayButton class:
https://github.com/ExpressLRS/ExpressLRS/tree/d9f56f8bd6f9f7144d5f01caaca766383e1e0950/src/lib/SCREEN/FiveWayButton
*/
#pragma once
#include "configuration.h"
#ifdef INPUTBROKER_EXPRESSLRSFIVEWAY_TYPE
#include <esp_adc_cal.h>
#include <soc/adc_channel.h>
#include "InputBroker.h"
#include "MeshService.h" // For adhoc ping action
#include "buzz.h"
#include "concurrency/OSThread.h"
#include "graphics/Screen.h" // Feedback for adhoc ping / toggle GPS
#include "main.h"
#include "modules/CannedMessageModule.h"
#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h" // For toggle GPS action
#endif
class ExpressLRSFiveWay : public Observable<const InputEvent *>, public concurrency::OSThread
{
private:
// Number of values in JOY_ADC_VALUES, if defined
// These must be ADC readings for {UP, DOWN, LEFT, RIGHT, ENTER, IDLE}
static constexpr size_t N_JOY_ADC_VALUES = 6;
static constexpr uint32_t KEY_DEBOUNCE_MS = 25;
static constexpr uint32_t KEY_LONG_PRESS_MS = 3000; // How many milliseconds to hold key for a long press
// This merged an enum used by the ExpressLRS code, with meshtastic canned message values
// Key names are kept simple, to allow user customizaton
typedef enum {
UP = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP,
DOWN = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN,
LEFT = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT,
RIGHT = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT,
OK = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT,
CANCEL = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL,
NO_PRESS = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE
} KeyType;
typedef enum { SHORT, LONG } PressLength;
// From ExpressLRS
int keyInProcess;
uint32_t keyDownStart;
bool isLongPressed;
const uint16_t joyAdcValues[N_JOY_ADC_VALUES] = {JOYSTICK_ADC_VALS};
uint16_t fuzzValues[N_JOY_ADC_VALUES];
void calcFuzzValues();
int readKey();
void update(int *keyValue, bool *keyLongPressed);
// Meshtastic code
void determineAction(KeyType key, PressLength length);
void sendKey(KeyType key);
inline bool inCannedMessageMenu() { return cannedMessageModule->shouldDraw(); }
int32_t runOnce() override;
// Simplified Meshtastic actions, for easier remapping by user
void toggleGPS();
void sendAdhocPing();
void shutdown();
void click();
bool alerting = false; // Is the screen showing an alert frame? Feedback for GPS toggle / adhoc ping actions
uint32_t alertingSinceMs = 0; // When did screen begin showing an alert frame? Used to auto-dismiss
public:
ExpressLRSFiveWay();
};
extern ExpressLRSFiveWay *expressLRSFiveWayInput;
#endif

204
src/input/ScanAndSelect.cpp Normal file
View File

@@ -0,0 +1,204 @@
#include "configuration.h"
// Normally these input methods are protected by guarding in setupModules
// In order to have the user button dismiss the canned message frame, this class lightly interacts with the Screen class
#if HAS_SCREEN
#include "ScanAndSelect.h"
#include "modules/CannedMessageModule.h"
// Config
static const char name[] = "scanAndSelect"; // should match "allow input source" string
static constexpr uint32_t durationShortMs = 50;
static constexpr uint32_t durationLongMs = 1500;
static constexpr uint32_t durationAlertMs = 2000;
// Constructor: init base class
ScanAndSelectInput::ScanAndSelectInput() : concurrency::OSThread(name) {}
// Attempt to setup class; true if success.
// Called by setupModules method. Instance deleted if setup fails.
bool ScanAndSelectInput::init()
{
// Short circuit: Canned messages enabled?
if (!moduleConfig.canned_message.enabled)
return false;
// Short circuit: Using correct "input source"?
// Todo: protobuf enum instead of string?
if (strcasecmp(moduleConfig.canned_message.allow_input_source, name) != 0)
return false;
// Use any available inputbroker pin as the button
if (moduleConfig.canned_message.inputbroker_pin_press)
pin = moduleConfig.canned_message.inputbroker_pin_press;
else if (moduleConfig.canned_message.inputbroker_pin_a)
pin = moduleConfig.canned_message.inputbroker_pin_a;
else if (moduleConfig.canned_message.inputbroker_pin_b)
pin = moduleConfig.canned_message.inputbroker_pin_b;
else
return false; // Short circuit: no button found
// Set-up the button
pinMode(pin, INPUT_PULLUP);
attachInterrupt(pin, handleChangeInterrupt, CHANGE);
// Connect our class to the canned message module
inputBroker->registerSource(this);
LOG_INFO("Initialized 'Scan and Select' input for Canned Messages, using pin %d\n", pin);
return true; // Init succeded
}
// Runs periodically, unless sleeping between presses
int32_t ScanAndSelectInput::runOnce()
{
uint32_t now = millis();
// If: "no messages added" alert screen currently shown
if (alertingNoMessage) {
// Dismiss the alert screen several seconds after it appears
if (now > alertingSinceMs + durationAlertMs) {
alertingNoMessage = false;
screen->endAlert();
}
}
// If: Button is pressed
if (digitalRead(pin) == LOW) {
// New press
if (!held) {
downSinceMs = now;
}
// Existing press
else {
// Duration enough for long press
// Long press not yet fired (prevent repeat firing while held)
if (!longPressFired && now - downSinceMs > durationLongMs) {
longPressFired = true;
longPress();
}
}
// Record the change of state: button is down
held = true;
}
// If: Button is not pressed
else {
// Button newly released
// Long press event didn't already fire
if (held && !longPressFired) {
// Duration enough for short press
if (now - downSinceMs > durationShortMs) {
shortPress();
}
}
// Record the change of state: button is up
held = false;
longPressFired = false; // Re-Arm: allow another long press
}
// If thread's job is done, let it sleep
if (!held && !alertingNoMessage) {
Thread::canSleep = true;
return OSThread::disable();
}
// Run this method again is a few ms
return durationShortMs;
}
void ScanAndSelectInput::longPress()
{
// (If canned messages set)
if (cannedMessageModule->hasMessages()) {
// If module frame displayed already, send the current message
if (cannedMessageModule->shouldDraw())
raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT);
// Otherwise, initial long press opens the module frame
else
raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN);
}
// (If canned messages not set) tell the user
else
alertNoMessage();
}
void ScanAndSelectInput::shortPress()
{
// (If canned messages set) scroll to next message
if (cannedMessageModule->hasMessages())
raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN);
// (If canned messages not yet set) tell the user
else
alertNoMessage();
}
// Begin running runOnce at regular intervals
// Called from pin change interrupt
void ScanAndSelectInput::enableThread()
{
Thread::canSleep = false;
OSThread::enabled = true;
OSThread::setIntervalFromNow(0);
}
// Inform user (screen) that no canned messages have been added
// Automatically dismissed after several seconds
void ScanAndSelectInput::alertNoMessage()
{
alertingNoMessage = true;
alertingSinceMs = millis();
// Graphics code: the alert frame to show on screen
screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
display->setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
display->setFont(FONT_SMALL);
int16_t textX = display->getWidth() / 2;
int16_t textY = display->getHeight() / 2;
display->drawString(textX + x, textY + y, "No Canned Messages");
});
}
// Remove the canned message frame from screen
// Used to dismiss the module frame when user button pressed
// Returns true if the frame was previously displayed, and has now been closed
// Return value consumed by Screen class when determining how to handle user button
bool ScanAndSelectInput::dismissCannedMessageFrame()
{
if (cannedMessageModule->shouldDraw()) {
raiseEvent(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL);
return true;
}
return false;
}
// Feed input to the canned messages module
void ScanAndSelectInput::raiseEvent(_meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar key)
{
InputEvent e;
e.source = name;
e.inputEvent = key;
notifyObservers(&e);
}
// Pin change interrupt
void ScanAndSelectInput::handleChangeInterrupt()
{
// Because we need to detect both press and release (rising and falling edge), the interrupt itself can't determine the
// action. Instead, we start up the thread and get it to read the button for us
// The instance we're referring to here is created in setupModules()
scanAndSelectInput->enableThread();
}
ScanAndSelectInput *scanAndSelectInput = nullptr; // Instantiated in setupModules method. Deleted if unused, or init() fails
#endif

50
src/input/ScanAndSelect.h Normal file
View File

@@ -0,0 +1,50 @@
/*
A "single button" input method for Canned Messages
- Short press to cycle through messages
- Long Press to send
To use:
- set "allow input source" to "scanAndSelect"
- set the single button's GPIO as either pin A, pin B, or pin Press
Originally designed to make use of "extra" built-in button on some boards.
Non-intrusive; suitable for use as a default module config.
*/
#pragma once
#include "concurrency/OSThread.h"
#include "main.h"
// Normally these input methods are protected by guarding in setupModules
// In order to have the user button dismiss the canned message frame, this class lightly interacts with the Screen class
#if HAS_SCREEN
class ScanAndSelectInput : public Observable<const InputEvent *>, public concurrency::OSThread
{
public:
ScanAndSelectInput(); // No-op constructor, only initializes OSThread base class
bool init(); // Attempt to setup class; true if success. Instance deleted if setup fails
bool dismissCannedMessageFrame(); // Remove the canned message frame from screen. True if frame was open, and now closed.
void alertNoMessage(); // Inform user (screen) that no canned messages have been added
protected:
int32_t runOnce() override; // Runs at regular intervals, when enabled
void enableThread(); // Begin running runOnce at regular intervals
static void handleChangeInterrupt(); // Calls enableThread from pin change interrupt
void shortPress(); // Code to run when short press fires
void longPress(); // Code to run when long press fires
void raiseEvent(_meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar key); // Feed input to canned message module
bool held = false; // Have we handled a change in button state?
bool longPressFired = false; // Long press fires while button still held. This bool ensures the release is no-op
uint32_t downSinceMs = 0; // Debouncing for short press, timing for long press
uint8_t pin = -1; // Read from cannned message config during init
bool alertingNoMessage = false; // Is the "no canned messages" alert shown on screen?
uint32_t alertingSinceMs = 0; // Used to dismiss the "no canned message" alert several seconds
};
extern ScanAndSelectInput *scanAndSelectInput; // Instantiated in setupModules method. Deleted if unused, or init() fails
#endif

View File

@@ -0,0 +1,170 @@
#include "SerialKeyboard.h"
#include "configuration.h"
#ifdef INPUTBROKER_SERIAL_TYPE
#define CANNED_MESSAGE_MODULE_ENABLE 1 // in case it's not set in the variant file
#if INPUTBROKER_SERIAL_TYPE == 1 // It's a Chatter
// 3 SHIFT level (lower case, upper case, numbers), up to 4 repeated presses, button number
unsigned char KeyMap[3][4][10] = {{{'.', 'a', 'd', 'g', 'j', 'm', 'p', 't', 'w', ' '},
{',', 'b', 'e', 'h', 'k', 'n', 'q', 'u', 'x', ' '},
{'?', 'c', 'f', 'i', 'l', 'o', 'r', 'v', 'y', ' '},
{'1', '2', '3', '4', '5', '6', 's', '8', 'z', ' '}}, // low case
{{'!', 'A', 'D', 'G', 'J', 'M', 'P', 'T', 'W', ' '},
{'+', 'B', 'E', 'H', 'K', 'N', 'Q', 'U', 'X', ' '},
{'-', 'C', 'F', 'I', 'L', 'O', 'R', 'V', 'Y', ' '},
{'1', '2', '3', '4', '5', '6', 'S', '8', 'Z', ' '}}, // upper case
{{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'},
{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'},
{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'},
{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}}}; // numbers
#endif
SerialKeyboard::SerialKeyboard(const char *name) : concurrency::OSThread(name)
{
this->_originName = name;
}
void SerialKeyboard::erase()
{
InputEvent e;
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
e.kbchar = 0x08;
e.source = this->_originName;
this->notifyObservers(&e);
}
int32_t SerialKeyboard::runOnce()
{
if (!INPUTBROKER_SERIAL_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;
pinMode(KB_LOAD, OUTPUT);
pinMode(KB_CLK, OUTPUT);
pinMode(KB_DATA, INPUT);
digitalWrite(KB_LOAD, HIGH);
digitalWrite(KB_CLK, LOW);
prevKeys = 0b1111111111111111;
LOG_DEBUG("Serial Keyboard setup\n");
}
if (INPUTBROKER_SERIAL_TYPE == 1) { // Chatter V1.0 & V2.0 keypads
// scan for keypresses
// Write pulse to load pin
digitalWrite(KB_LOAD, LOW);
delayMicroseconds(5);
digitalWrite(KB_LOAD, HIGH);
delayMicroseconds(5);
// Get data from 74HC165
byte shiftRegister1 = shiftIn(KB_DATA, KB_CLK, LSBFIRST);
byte shiftRegister2 = shiftIn(KB_DATA, KB_CLK, LSBFIRST);
keys = (shiftRegister1 << 8) + shiftRegister2;
// Print to serial monitor
// Serial.print (shiftRegister1, BIN);
// Serial.print ("X");
// Serial.println (shiftRegister2, BIN);
if (millis() - lastPressTime > 500) {
quickPress = 0;
}
if (keys < prevKeys) { // a new key has been pressed (and not released), doesn't works for multiple presses at once but
// shouldn't be a limitation
InputEvent e;
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
e.source = this->_originName;
// SELECT OR SEND OR CANCEL EVENT
if (!(shiftRegister2 & (1 << 3))) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
} else if (!(shiftRegister2 & (1 << 2))) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
e.kbchar = 0xb7;
} else if (!(shiftRegister2 & (1 << 1))) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
} else if (!(shiftRegister2 & (1 << 0))) {
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
}
// TEXT INPUT EVENT
else if (!(shiftRegister1 & (1 << 4))) {
keyPressed = 0;
} else if (!(shiftRegister1 & (1 << 3))) {
keyPressed = 1;
} else if (!(shiftRegister2 & (1 << 4))) {
keyPressed = 2;
} else if (!(shiftRegister1 & (1 << 5))) {
keyPressed = 3;
} else if (!(shiftRegister1 & (1 << 2))) {
keyPressed = 4;
} else if (!(shiftRegister2 & (1 << 5))) {
keyPressed = 5;
} else if (!(shiftRegister1 & (1 << 6))) {
keyPressed = 6;
} else if (!(shiftRegister1 & (1 << 1))) {
keyPressed = 7;
} else if (!(shiftRegister2 & (1 << 6))) {
keyPressed = 8;
} else if (!(shiftRegister1 & (1 << 0))) {
keyPressed = 9;
}
// BACKSPACE or TAB
else if (!(shiftRegister1 & (1 << 7))) {
if (shift == 0 || shift == 2) { // BACKSPACE
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
e.kbchar = 0x08;
} else { // shift = 1 => TAB
e.inputEvent = ANYKEY;
e.kbchar = 0x09;
}
}
// SHIFT
else if (!(shiftRegister2 & (1 << 7))) {
keyPressed = 10;
}
if (keyPressed < 11) {
if (keyPressed == lastKeyPressed && millis() - lastPressTime < 500) {
quickPress += 1;
if (quickPress > 3) {
quickPress = 0;
}
}
if (keyPressed != lastKeyPressed) {
quickPress = 0;
}
if (keyPressed < 10) { // if it's a letter
if (keyPressed == lastKeyPressed && millis() - lastPressTime < 500) {
erase();
}
e.inputEvent = ANYKEY;
e.kbchar = char(KeyMap[shift][quickPress][keyPressed]);
} else { // then it's shift
shift += 1;
if (shift > 2) {
shift = 0;
}
}
lastPressTime = millis();
lastKeyPressed = keyPressed;
keyPressed = 13;
}
if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
this->notifyObservers(&e);
}
}
prevKeys = keys;
}
return 50;
}
#endif // INPUTBROKER_SERIAL_TYPE

View File

@@ -0,0 +1,25 @@
#pragma once
#include "InputBroker.h"
#include "concurrency/OSThread.h"
class SerialKeyboard : public Observable<const InputEvent *>, public concurrency::OSThread
{
public:
explicit SerialKeyboard(const char *name);
protected:
virtual int32_t runOnce() override;
void erase();
private:
const char *_originName;
bool firstTime = 1;
int prevKeys = 0;
int keys = 0;
int shift = 0;
int keyPressed = 13;
int lastKeyPressed = 13;
int quickPress = 0;
unsigned long lastPressTime = 0;
};

View File

@@ -0,0 +1,21 @@
#include "SerialKeyboardImpl.h"
#include "InputBroker.h"
#include "configuration.h"
#ifdef INPUTBROKER_SERIAL_TYPE
SerialKeyboardImpl *aSerialKeyboardImpl;
SerialKeyboardImpl::SerialKeyboardImpl() : SerialKeyboard("serialKB") {}
void SerialKeyboardImpl::init()
{
if (!INPUTBROKER_SERIAL_TYPE) {
disable();
return;
}
inputBroker->registerSource(this);
}
#endif // INPUTBROKER_SERIAL_TYPE

View File

@@ -0,0 +1,19 @@
#pragma once
#include "SerialKeyboard.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 SerialKeyboardImpl : public SerialKeyboard
{
public:
SerialKeyboardImpl();
void init();
};
extern SerialKeyboardImpl *aSerialKeyboardImpl;

View File

@@ -15,12 +15,17 @@
#include "power.h"
// #include "debug.h"
#include "FSCommon.h"
#include "Led.h"
#include "RTC.h"
#include "SPILock.h"
#include "concurrency/OSThread.h"
#include "concurrency/Periodic.h"
#include "detect/ScanI2C.h"
#if !MESHTASTIC_EXCLUDE_I2C
#include "detect/ScanI2CTwoWire.h"
#include <Wire.h>
#endif
#include "detect/axpDebug.h"
#include "detect/einkScan.h"
#include "graphics/RAKled.h"
@@ -31,7 +36,6 @@
#include "shutdown.h"
#include "sleep.h"
#include "target_specific.h"
#include <Wire.h>
#include <memory>
#include <utility>
// #include <driver/rtc_io.h>
@@ -159,8 +163,10 @@ bool pauseBluetoothLogging = false;
bool pmu_found;
#if !MESHTASTIC_EXCLUDE_I2C
// Array map of sensor types with i2c address and wire as we'll find in the i2c scan
std::pair<uint8_t, TwoWire *> nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1] = {};
#endif
Router *router = NULL; // Users of router don't care what sort of subclass implements that API
@@ -192,7 +198,7 @@ static int32_t ledBlinker()
static bool ledOn;
ledOn ^= 1;
setLed(ledOn);
ledBlink.set(ledOn);
// have a very sparse duty cycle of LED being on, unless charging, then blink 0.5Hz square wave rate to indicate that
return powerStatus->getIsCharging() ? 1000 : (ledOn ? 1 : 1000);
@@ -203,7 +209,6 @@ uint32_t timeLastPowered = 0;
static Periodic *ledPeriodic;
static OSThread *powerFSMthread;
static OSThread *ambientLightingThread;
SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
RadioInterface *rIf = NULL;
@@ -226,6 +231,12 @@ void printInfo()
void setup()
{
concurrency::hasBeenSetup = true;
#if ARCH_PORTDUINO
SPISettings spiSettings(settingsMap[spiSpeed], MSBFIRST, SPI_MODE0);
#else
SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
#endif
meshtastic_Config_DisplayConfig_OledType screen_model =
meshtastic_Config_DisplayConfig_OledType::meshtastic_Config_DisplayConfig_OledType_OLED_AUTO;
OLEDDISPLAY_GEOMETRY screen_geometry = GEOMETRY_128_64;
@@ -308,6 +319,14 @@ void setup()
digitalWrite(RESET_OLED, 1);
#endif
#ifdef SENSOR_POWER_CTRL_PIN
pinMode(SENSOR_POWER_CTRL_PIN, OUTPUT);
digitalWrite(SENSOR_POWER_CTRL_PIN, SENSOR_POWER_ON);
#endif
#ifdef SENSOR_GPS_CONFLICT
bool sensor_detected = false;
#endif
#ifdef PERIPHERAL_WARMUP_MS
// Some peripherals may require additional time to stabilize after power is connected
// e.g. I2C on Heltec Vision Master
@@ -349,6 +368,7 @@ void setup()
#endif
#if !MESHTASTIC_EXCLUDE_I2C
#if defined(I2C_SDA1) && defined(ARCH_RP2040)
Wire1.setSDA(I2C_SDA1);
Wire1.setSCL(I2C_SCL1);
@@ -373,6 +393,7 @@ void setup()
#elif HAS_WIRE
Wire.begin();
#endif
#endif
#ifdef PIN_LCD_RESET
// FIXME - move this someplace better, LCD is at address 0x3F
@@ -405,6 +426,7 @@ void setup()
powerStatus->observe(&power->newStatus);
power->setup(); // Must be after status handler is installed, so that handler gets notified of the initial configuration
#if !MESHTASTIC_EXCLUDE_I2C
// We need to scan here to decide if we have a screen for nodeDB.init() and because power has been applied to
// accessories
auto i2cScanner = std::unique_ptr<ScanI2CTwoWire>(new ScanI2CTwoWire());
@@ -444,6 +466,9 @@ void setup()
LOG_INFO("No I2C devices found\n");
} else {
LOG_INFO("%i I2C devices found\n", i2cCount);
#ifdef SENSOR_GPS_CONFLICT
sensor_detected = true;
#endif
}
#ifdef ARCH_ESP32
@@ -560,6 +585,7 @@ void setup()
SCANNER_TO_SENSORS_MAP(ScanI2C::DeviceType::DFROBOT_LARK, meshtastic_TelemetrySensorType_DFROBOT_LARK)
i2cScanner.reset();
#endif
#ifdef HAS_SDCARD
setupSDCard();
@@ -569,7 +595,7 @@ void setup()
#ifdef LED_PIN
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, 1 ^ LED_INVERTED); // turn on for now
digitalWrite(LED_PIN, LED_STATE_ON); // turn on for now
#endif
// Hello
@@ -620,6 +646,7 @@ void setup()
screen_model = meshtastic_Config_DisplayConfig_OledType_OLED_SH1107; // keep dimension of 128x64
#endif
#if !MESHTASTIC_EXCLUDE_I2C
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
if (acc_info.type != ScanI2C::DeviceType::NONE) {
config.display.wake_on_tap_or_motion = true;
@@ -635,6 +662,7 @@ void setup()
ambientLightingThread = new AmbientLightingThread(rgb_found.type);
}
#endif
#endif
#ifdef T_WATCH_S3
drv.begin();
@@ -672,6 +700,7 @@ void setup()
screen = new graphics::Screen(screen_found, screen_model, screen_geometry);
// setup TZ prior to time actions.
#if !MESHTASTIC_EXCLUDE_TZ
if (*config.device.tzdef) {
setenv("TZ", config.device.tzdef, 1);
} else {
@@ -679,22 +708,30 @@ void setup()
}
tzset();
LOG_DEBUG("Set Timezone to %s\n", getenv("TZ"));
#endif
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
#if !MESHTASTIC_EXCLUDE_GPS
// If we're taking on the repeater role, ignore GPS
if (HAS_GPS) {
if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) {
gps = GPS::createGps();
if (gps) {
gpsStatus->observe(&gps->newStatus);
} else {
LOG_DEBUG("Running without GPS.\n");
#ifdef SENSOR_GPS_CONFLICT
if (sensor_detected == false) {
#endif
if (HAS_GPS) {
if (config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_NOT_PRESENT) {
gps = GPS::createGps();
if (gps) {
gpsStatus->observe(&gps->newStatus);
} else {
LOG_DEBUG("Running without GPS.\n");
}
}
}
#ifdef SENSOR_GPS_CONFLICT
}
#endif
#endif
nodeStatus->observe(&nodeDB->newStatus);
@@ -703,8 +740,8 @@ void setup()
LOG_DEBUG("Starting audio thread\n");
audioThread = new AudioThread();
#endif
service.init();
service = new MeshService();
service->init();
// Now that the mesh service is created, create any modules
setupModules();
@@ -712,7 +749,7 @@ void setup()
#ifdef LED_PIN
// Turn LED off after boot, if heartbeat by config
if (config.device.led_heartbeat_disabled)
digitalWrite(LED_PIN, LOW ^ LED_INVERTED);
digitalWrite(LED_PIN, HIGH ^ LED_STATE_ON);
#endif
// Do this after service.init (because that clears error_code)
@@ -721,6 +758,7 @@ void setup()
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_NO_AXP192); // Record a hardware fault for missing hardware
#endif
#if !MESHTASTIC_EXCLUDE_I2C
// Don't call screen setup until after nodedb is setup (because we need
// the current region name)
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS) || \
@@ -733,6 +771,7 @@ void setup()
#else
if (screen_found.port != ScanI2C::I2CPort::NO_I2C)
screen->setup();
#endif
#endif
screen->print("Started...\n");
@@ -960,9 +999,16 @@ void setup()
mqttInit();
#endif
#ifdef RF95_FAN_EN
// Ability to disable FAN if PIN has been set with RF95_FAN_EN.
// Make sure LoRa has been started before disabling FAN.
if (config.lora.pa_fan_disabled)
digitalWrite(RF95_FAN_EN, LOW ^ 0);
#endif
#ifndef ARCH_PORTDUINO
// Initialize Wifi
// Initialize Wifi
#if HAS_WIFI
initWifi();
#endif
@@ -1060,7 +1106,7 @@ void loop()
// TODO: This should go into a thread handled by FreeRTOS.
// handleWebResponse();
service.loop();
service->loop();
long delayMsec = mainController.runOrDelay();

View File

@@ -65,12 +65,6 @@ extern bool isVibrating;
extern int TCPPort; // set by Portduino
// extern Observable<meshtastic::PowerStatus> newPowerStatus; //TODO: move this to main-esp32.cpp somehow or a helper class
// extern meshtastic::PowerStatus *powerStatus;
// extern meshtastic::GPSStatus *gpsStatus;
// extern meshtastic::NodeStatusHandler *nodeStatusHandler;
// Return a human readable string of the form "Meshtastic_ab13"
const char *getDeviceName();
@@ -91,5 +85,5 @@ void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearB
meshtastic_DeviceMetadata getDeviceMetadata();
// FIXME, we default to 4MHz SPI, SPI mode 0, check if the datasheet says it can really do that
extern SPISettings spiSettings;
// We default to 4MHz SPI, SPI mode 0
extern SPISettings spiSettings;

View File

@@ -1,5 +1,7 @@
#include "Channels.h"
#include "../userPrefs.h"
#include "CryptoEngine.h"
#include "Default.h"
#include "DisplayFormatters.h"
#include "NodeDB.h"
#include "RadioInterface.h"
@@ -90,6 +92,7 @@ void Channels::initDefaultChannel(ChannelIndex chIndex)
loraConfig.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST; // Default to Long Range & Fast
loraConfig.use_preset = true;
loraConfig.tx_power = 0; // default
loraConfig.channel_num = 0;
uint8_t defaultpskIndex = 1;
channelSettings.psk.bytes[0] = defaultpskIndex;
channelSettings.psk.size = 1;
@@ -99,6 +102,29 @@ void Channels::initDefaultChannel(ChannelIndex chIndex)
ch.has_settings = true;
ch.role = meshtastic_Channel_Role_PRIMARY;
#ifdef LORACONFIG_MODEM_PRESET_USERPREFS
loraConfig.modem_preset = LORACONFIG_MODEM_PRESET_USERPREFS;
#endif
#ifdef LORACONFIG_CHANNEL_NUM_USERPREFS
loraConfig.channel_num = LORACONFIG_CHANNEL_NUM_USERPREFS;
#endif
// Install custom defaults. Will eventually support setting multiple default channels
if (chIndex == 0) {
#ifdef CHANNEL_0_PSK_USERPREFS
static const uint8_t defaultpsk[] = CHANNEL_0_PSK_USERPREFS;
memcpy(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk));
channelSettings.psk.size = sizeof(defaultpsk);
#endif
#ifdef CHANNEL_0_NAME_USERPREFS
strcpy(channelSettings.name, CHANNEL_0_NAME_USERPREFS);
#endif
#ifdef CHANNEL_0_PRECISION_USERPREFS
channelSettings.module_settings.position_precision = CHANNEL_0_PRECISION_USERPREFS;
#endif
}
}
CryptoKey Channels::getKey(ChannelIndex chIndex)
@@ -251,6 +277,12 @@ void Channels::setChannel(const meshtastic_Channel &c)
bool Channels::anyMqttEnabled()
{
#if EVENT_MODE
// Don't publish messages on the public MQTT broker if we are in event mode
if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0) {
return false;
}
#endif
for (int i = 0; i < getNumChannels(); i++)
if (channelFile.channels[i].role != meshtastic_Channel_Role_DISABLED && channelFile.channels[i].has_settings &&
(channelFile.channels[i].settings.downlink_enabled || channelFile.channels[i].settings.uplink_enabled))
@@ -277,12 +309,14 @@ const char *Channels::getName(size_t chIndex)
return channelName;
}
bool Channels::isDefaultChannel(const meshtastic_Channel &ch)
bool Channels::isDefaultChannel(ChannelIndex chIndex)
{
const auto &ch = getByIndex(chIndex);
if (ch.settings.psk.size == 1 && ch.settings.psk.bytes[0] == 1) {
const char *name = getName(chIndex);
const char *presetName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
// Check if the name is the default derived from the modem preset
if (strcmp(ch.settings.name, presetName) == 0)
if (strcmp(name, presetName) == 0)
return true;
}
return false;
@@ -295,8 +329,7 @@ bool Channels::hasDefaultChannel()
return false;
// Check if any of the channels are using the default name and PSK
for (size_t i = 0; i < getNumChannels(); i++) {
const auto &ch = getByIndex(i);
if (isDefaultChannel(ch))
if (isDefaultChannel(i))
return true;
}
return false;
@@ -330,4 +363,4 @@ bool Channels::decryptForHash(ChannelIndex chIndex, ChannelHash channelHash)
int16_t Channels::setActiveByIndex(ChannelIndex channelIndex)
{
return setCrypto(channelIndex);
}
}

View File

@@ -84,7 +84,7 @@ class Channels
int16_t setActiveByIndex(ChannelIndex channelIndex);
// Returns true if the channel has the default name and PSK
bool isDefaultChannel(const meshtastic_Channel &ch);
bool isDefaultChannel(ChannelIndex chIndex);
// Returns true if we can be reached via a channel with the default settings given a region and modem preset
bool hasDefaultChannel();
@@ -129,4 +129,4 @@ class Channels
};
/// Singleton channel table
extern Channels channels;
extern Channels channels;

View File

@@ -1,4 +1,5 @@
#include "Default.h"
#include "../userPrefs.h"
uint32_t Default::getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval)
{
@@ -40,4 +41,13 @@ uint32_t Default::getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t d
return getConfiguredOrDefaultMs(configured, defaultValue);
return getConfiguredOrDefaultMs(configured, defaultValue) * congestionScalingCoefficient(numOnlineNodes);
}
uint8_t Default::getConfiguredOrDefaultHopLimit(uint8_t configured)
{
#if EVENT_MODE
return (configured > HOP_RELIABLE) ? HOP_RELIABLE : config.lora.hop_limit;
#else
return (configured >= HOP_MAX) ? HOP_MAX : config.lora.hop_limit;
#endif
}

View File

@@ -13,7 +13,9 @@
#define default_min_wake_secs 10
#define default_screen_on_secs IF_ROUTER(1, 60 * 10)
#define default_node_info_broadcast_secs 3 * 60 * 60
#define default_neighbor_info_broadcast_secs 6 * 60 * 60
#define min_node_info_broadcast_secs 60 * 60 // No regular broadcasts of more than once an hour
#define min_neighbor_info_broadcast_secs 2 * 60 * 60
#define default_mqtt_address "mqtt.meshtastic.org"
#define default_mqtt_username "meshdev"
@@ -30,6 +32,7 @@ class Default
static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval);
static uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue);
static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes);
static uint8_t getConfiguredOrDefaultHopLimit(uint8_t configured);
private:
static float congestionScalingCoefficient(int numOnlineNodes)

View File

@@ -1,4 +1,5 @@
#include "FloodingRouter.h"
#include "../userPrefs.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
@@ -46,6 +47,13 @@ void FloodingRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it
tosend->hop_limit--; // bump down the hop count
#if EVENT_MODE
if (tosend->hop_limit > 2) {
// if we are "correcting" the hop_limit, "correct" the hop_start by the same amount to preserve hops away.
tosend->hop_start -= (tosend->hop_limit - 2);
tosend->hop_limit = 2;
}
#endif
LOG_INFO("Rebroadcasting received floodmsg to neighbors\n");
// Note: we are careful to resend using the original senders node id

View File

@@ -100,7 +100,13 @@ template <typename T> bool LR11x0Interface<T>::init()
// FIXME: May want to set depending on a definition, currently all LR1110 variant files use the DC-DC regulator option
if (res == RADIOLIB_ERR_NONE)
res = lora.setRegulatorDCDC();
#ifdef TRACKER_T1000_E
#ifdef LR11X0_DIO_RF_SWITCH_CONFIG
res = lora.setDioAsRfSwitch(LR11X0_DIO_RF_SWITCH_CONFIG);
#else
res = lora.setDioAsRfSwitch(0x03, 0x0, 0x01, 0x03, 0x02, 0x0, 0x0, 0x0);
#endif
#endif
if (res == RADIOLIB_ERR_NONE) {
if (config.lora.sx126x_rx_boosted_gain) { // the name is unfortunate but historically accurate
res = lora.setRxBoostedGainMode(true);
@@ -279,17 +285,15 @@ template <typename T> bool LR11x0Interface<T>::isActivelyReceiving()
template <typename T> bool LR11x0Interface<T>::sleep()
{
// Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet
// \todo Display actual typename of the adapter, not just `LR11x0`
LOG_DEBUG("LR11x0 entering sleep mode (FIXME, don't keep config)\n");
LOG_DEBUG("LR11x0 entering sleep mode\n");
setStandby(); // Stop any pending operations
// turn off TCXO if it was powered
// FIXME - this isn't correct
// lora.setTCXO(0);
lora.setTCXO(0);
// put chipset into sleep mode (we've already disabled interrupts by now)
bool keepConfig = true;
bool keepConfig = false;
lora.sleep(keepConfig, 0); // Note: we do not keep the config, full reinit will be needed
#ifdef LR11X0_POWER_EN

View File

@@ -170,7 +170,7 @@ void MeshModule::callModules(meshtastic_MeshPacket &mp, RxSource src)
if (isDecoded && mp.decoded.want_response && toUs) {
if (currentReply) {
printPacket("Sending response", currentReply);
service.sendToMesh(currentReply);
service->sendToMesh(currentReply);
currentReply = NULL;
} else if (mp.from != ourNodeNum && !ignoreRequest) {
// Note: if the message started with the local node or a module asked to ignore the request, we don't want to send a

View File

@@ -19,8 +19,8 @@
#include <assert.h>
#include <string>
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
#include "nimble/NimbleBluetooth.h"
#if ARCH_PORTDUINO
#include "PortduinoGlue.h"
#endif
/*
@@ -40,20 +40,9 @@ arbitrating to select a node number and keeping the current nodedb.
The algorithm is as follows:
* when a node starts up, it broadcasts their user and the normal flow is for all other nodes to reply with their User as well (so
the new node can build its node db)
* If a node ever receives a User (not just the first broadcast) message where the sender node number equals our node number, that
indicates a collision has occurred and the following steps should happen:
If the receiving node (that was already in the mesh)'s macaddr is LOWER than the new User who just tried to sign in: it gets to
keep its nodenum. We send a broadcast message of OUR User (we use a broadcast so that the other node can receive our message,
considering we have the same id - it also serves to let observers correct their nodedb) - this case is rare so it should be okay.
If any node receives a User where the macaddr is GTE than their local macaddr, they have been vetoed and should pick a new random
nodenum (filtering against whatever it knows about the nodedb) and rebroadcast their User.
FIXME in the initial proof of concept we just skip the entire want/deny flow and just hand pick node numbers at first.
*/
MeshService service;
MeshService *service;
static MemoryDynamic<meshtastic_MqttClientProxyMessage> staticMqttClientProxyMessagePool;
@@ -73,8 +62,6 @@ MeshService::MeshService()
void MeshService::init()
{
// moved much earlier in boot (called from setup())
// nodeDB.init();
#if HAS_GPS
if (gps)
gpsObserver.observe(&gps->newStatus);
@@ -401,4 +388,4 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
bool MeshService::isToPhoneQueueEmpty()
{
return toPhoneQueue.isEmpty();
}
}

View File

@@ -150,4 +150,4 @@ class MeshService
friend class RoutingModule;
};
extern MeshService service;
extern MeshService *service;

View File

@@ -1,3 +1,4 @@
#include "../userPrefs.h"
#include "configuration.h"
#if !MESHTASTIC_EXCLUDE_GPS
#include "GPS.h"
@@ -13,6 +14,7 @@
#include "PowerFSM.h"
#include "RTC.h"
#include "Router.h"
#include "SafeFile.h"
#include "TypeConversions.h"
#include "error.h"
#include "main.h"
@@ -52,6 +54,7 @@ meshtastic_LocalConfig config;
meshtastic_LocalModuleConfig moduleConfig;
meshtastic_ChannelFile channelFile;
meshtastic_OEMStore oemStore;
static bool hasOemStore = false;
bool meshtastic_DeviceState_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field)
{
@@ -187,14 +190,16 @@ bool NodeDB::resetRadioConfig(bool factory_reset)
return didFactoryReset;
}
bool NodeDB::factoryReset()
bool NodeDB::factoryReset(bool eraseBleBonds)
{
LOG_INFO("Performing factory reset!\n");
// first, remove the "/prefs" (this removes most prefs)
rmDir("/prefs");
#ifdef FSCom
if (FSCom.exists("/static/rangetest.csv") && !FSCom.remove("/static/rangetest.csv")) {
LOG_ERROR("Could not remove rangetest.csv file\n");
}
#endif
// second, install default state (this will deal with the duplicate mac address issue)
installDefaultDeviceState();
installDefaultConfig();
@@ -202,18 +207,21 @@ bool NodeDB::factoryReset()
installDefaultChannels();
// third, write everything to disk
saveToDisk();
if (eraseBleBonds) {
LOG_INFO("Erasing BLE bonds\n");
#ifdef ARCH_ESP32
// This will erase what's in NVS including ssl keys, persistent variables and ble pairing
nvs_flash_erase();
// This will erase what's in NVS including ssl keys, persistent variables and ble pairing
nvs_flash_erase();
#endif
#ifdef ARCH_NRF52
Bluefruit.begin();
LOG_INFO("Clearing bluetooth bonds!\n");
bond_print_list(BLE_GAP_ROLE_PERIPH);
bond_print_list(BLE_GAP_ROLE_CENTRAL);
Bluefruit.Periph.clearBonds();
Bluefruit.Central.clearBonds();
Bluefruit.begin();
LOG_INFO("Clearing bluetooth bonds!\n");
bond_print_list(BLE_GAP_ROLE_PERIPH);
bond_print_list(BLE_GAP_ROLE_CENTRAL);
Bluefruit.Periph.clearBonds();
Bluefruit.Central.clearBonds();
#endif
}
return true;
}
@@ -235,10 +243,22 @@ void NodeDB::installDefaultConfig()
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;
#ifdef CONFIG_LORA_REGION_USERPREFS
config.lora.region = CONFIG_LORA_REGION_USERPREFS;
#else
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
#endif
#ifdef LORACONFIG_MODEM_PRESET_USERPREFS
config.lora.modem_preset = LORACONFIG_MODEM_PRESET_USERPREFS;
#else
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
#endif
config.lora.hop_limit = HOP_RELIABLE;
#ifdef CONFIG_LORA_IGNORE_MQTT_USERPREFS
config.lora.ignore_mqtt = CONFIG_LORA_IGNORE_MQTT_USERPREFS;
#else
config.lora.ignore_mqtt = false;
#endif
#ifdef PIN_GPS_EN
config.position.gps_en_gpio = PIN_GPS_EN;
#endif
@@ -288,13 +308,17 @@ void NodeDB::installDefaultConfig()
meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING |
meshtastic_Config_PositionConfig_PositionFlags_DOP | meshtastic_Config_PositionConfig_PositionFlags_SATINVIEW);
#ifdef RADIOMASTER_900_BANDIT_NANO
#ifdef DISPLAY_FLIP_SCREEN
config.display.flip_screen = true;
#endif
#ifdef T_WATCH_S3
config.display.screen_on_secs = 30;
config.display.wake_on_tap_or_motion = true;
#endif
#ifdef HELTEC_VISION_MASTER_E290
// Orient so that LoRa antenna faces up
config.display.flip_screen = true;
#endif
initConfigIntervals();
}
@@ -352,6 +376,13 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.external_notification.output_ms = 100;
moduleConfig.external_notification.active = true;
#endif
#ifdef BUTTON_SECONDARY_CANNEDMESSAGES
// Use a board's second built-in button as input source for canned messages
moduleConfig.canned_message.enabled = true;
moduleConfig.canned_message.inputbroker_pin_press = BUTTON_PIN_SECONDARY;
strcpy(moduleConfig.canned_message.allow_input_source, "scanAndSelect");
#endif
moduleConfig.has_canned_message = true;
strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(moduleConfig.mqtt.address));
@@ -511,8 +542,16 @@ void NodeDB::installDefaultDeviceState()
// Set default owner name
pickNewNodeNum(); // based on macaddr now
#ifdef CONFIG_OWNER_LONG_NAME_USERPREFS
snprintf(owner.long_name, sizeof(owner.long_name), CONFIG_OWNER_LONG_NAME_USERPREFS);
#else
snprintf(owner.long_name, sizeof(owner.long_name), "Meshtastic %02x%02x", ourMacAddr[4], ourMacAddr[5]);
#endif
#ifdef CONFIG_OWNER_SHORT_NAME_USERPREFS
snprintf(owner.short_name, sizeof(owner.short_name), CONFIG_OWNER_SHORT_NAME_USERPREFS);
#else
snprintf(owner.short_name, sizeof(owner.short_name), "%02x%02x", ourMacAddr[4], ourMacAddr[5]);
#endif
snprintf(owner.id, sizeof(owner.id), "!%08x", getNodeNum()); // Default node ID now based on nodenum
memcpy(owner.macaddr, ourMacAddr, sizeof(owner.macaddr));
}
@@ -557,11 +596,6 @@ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t
LoadFileResult state = LoadFileResult::OTHER_FAILURE;
#ifdef FSCom
if (!FSCom.exists(filename)) {
LOG_INFO("File %s not found\n", filename);
return LoadFileResult::NOT_FOUND;
}
auto f = FSCom.open(filename, FILE_O_READ);
if (f) {
@@ -574,7 +608,7 @@ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t
state = LoadFileResult::DECODE_FAILED;
} else {
LOG_INFO("Loaded %s successfully\n", filename);
state = LoadFileResult::SUCCESS;
state = LoadFileResult::LOAD_SUCCESS;
}
f.close();
} else {
@@ -582,35 +616,43 @@ LoadFileResult NodeDB::loadProto(const char *filename, size_t protoSize, size_t
}
#else
LOG_ERROR("ERROR: Filesystem not implemented\n");
state = LoadFileState::NO_FILESYSTEM;
state = LoadFileResult::NO_FILESYSTEM;
#endif
return state;
}
void NodeDB::loadFromDisk()
{
devicestate.version =
0; // Mark the current device state as completely unusable, so that if we fail reading the entire file from
// disk we will still factoryReset to restore things.
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
auto state = loadProto(prefFileName, sizeof(meshtastic_DeviceState) + MAX_NUM_NODES * sizeof(meshtastic_NodeInfo),
sizeof(meshtastic_DeviceState), &meshtastic_DeviceState_msg, &devicestate);
if (state != LoadFileResult::SUCCESS) {
installDefaultDeviceState(); // Our in RAM copy might now be corrupt
// See https://github.com/meshtastic/firmware/issues/4184#issuecomment-2269390786
// It is very important to try and use the saved prefs even if we fail to read meshtastic_DeviceState. Because most of our
// critical config may still be valid (in the other files - loaded next).
// Also, if we did fail on reading we probably failed on the enormous (and non critical) nodeDB. So DO NOT install default
// device state.
// if (state != LoadFileResult::LOAD_SUCCESS) {
// installDefaultDeviceState(); // Our in RAM copy might now be corrupt
//} else {
if (devicestate.version < DEVICESTATE_MIN_VER) {
LOG_WARN("Devicestate %d is old, discarding\n", devicestate.version);
factoryReset();
} else {
if (devicestate.version < DEVICESTATE_MIN_VER) {
LOG_WARN("Devicestate %d is old, discarding\n", devicestate.version);
factoryReset();
} else {
LOG_INFO("Loaded saved devicestate version %d, with nodecount: %d\n", devicestate.version,
devicestate.node_db_lite.size());
meshNodes = &devicestate.node_db_lite;
numMeshNodes = devicestate.node_db_lite.size();
}
LOG_INFO("Loaded saved devicestate version %d, with nodecount: %d\n", devicestate.version,
devicestate.node_db_lite.size());
meshNodes = &devicestate.node_db_lite;
numMeshNodes = devicestate.node_db_lite.size();
}
meshNodes->resize(MAX_NUM_NODES);
state = loadProto(configFileName, meshtastic_LocalConfig_size, sizeof(meshtastic_LocalConfig), &meshtastic_LocalConfig_msg,
&config);
if (state != LoadFileResult::SUCCESS) {
if (state != LoadFileResult::LOAD_SUCCESS) {
installDefaultConfig(); // Our in RAM copy might now be corrupt
} else {
if (config.version < DEVICESTATE_MIN_VER) {
@@ -623,7 +665,7 @@ void NodeDB::loadFromDisk()
state = loadProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, sizeof(meshtastic_LocalModuleConfig),
&meshtastic_LocalModuleConfig_msg, &moduleConfig);
if (state != LoadFileResult::SUCCESS) {
if (state != LoadFileResult::LOAD_SUCCESS) {
installDefaultModuleConfig(); // Our in RAM copy might now be corrupt
} else {
if (moduleConfig.version < DEVICESTATE_MIN_VER) {
@@ -636,7 +678,7 @@ void NodeDB::loadFromDisk()
state = loadProto(channelFileName, meshtastic_ChannelFile_size, sizeof(meshtastic_ChannelFile), &meshtastic_ChannelFile_msg,
&channelFile);
if (state != LoadFileResult::SUCCESS) {
if (state != LoadFileResult::LOAD_SUCCESS) {
installDefaultChannels(); // Our in RAM copy might now be corrupt
} else {
if (channelFile.version < DEVICESTATE_MIN_VER) {
@@ -648,8 +690,9 @@ void NodeDB::loadFromDisk()
}
state = loadProto(oemConfigFile, meshtastic_OEMStore_size, sizeof(meshtastic_OEMStore), &meshtastic_OEMStore_msg, &oemStore);
if (state == LoadFileResult::SUCCESS) {
if (state == LoadFileResult::LOAD_SUCCESS) {
LOG_INFO("Loaded OEMStore\n");
hasOemStore = true;
}
// 2.4.X - configuration migration to update new default intervals
@@ -674,48 +717,26 @@ void NodeDB::loadFromDisk()
}
/** Save a protobuf from a file, return true for success */
bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct)
bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct,
bool fullAtomic)
{
bool okay = false;
#ifdef FSCom
// static DeviceState scratch; We no longer read into a tempbuf because this structure is 15KB of valuable RAM
String filenameTmp = filename;
filenameTmp += ".tmp";
auto f = FSCom.open(filenameTmp.c_str(), FILE_O_WRITE);
if (f) {
LOG_INFO("Saving %s\n", filename);
pb_ostream_t stream = {&writecb, &f, protoSize};
auto f = SafeFile(filename, fullAtomic);
if (!pb_encode(&stream, fields, dest_struct)) {
LOG_ERROR("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
} else {
okay = true;
}
f.flush();
f.close();
LOG_INFO("Saving %s\n", filename);
pb_ostream_t stream = {&writecb, static_cast<Print *>(&f), protoSize};
// brief window of risk here ;-)
if (FSCom.exists(filename) && !FSCom.remove(filename)) {
LOG_WARN("Can't remove old pref file\n");
}
if (!renameFile(filenameTmp.c_str(), filename)) {
LOG_ERROR("Error: can't rename new pref file\n");
}
if (!pb_encode(&stream, fields, dest_struct)) {
LOG_ERROR("Error: can't encode protobuf %s\n", PB_GET_ERROR(&stream));
} else {
LOG_ERROR("Can't write prefs\n");
#ifdef ARCH_NRF52
static uint8_t failedCounter = 0;
failedCounter++;
if (failedCounter >= 2) {
LOG_ERROR("Failed to save file twice. Rebooting...\n");
delay(100);
NVIC_SystemReset();
// We used to blow away the filesystem here, but that's a bit extreme
// FSCom.format();
// // After formatting, the device needs to be restarted
// nodeDB->resetRadioConfig(true);
}
#endif
okay = true;
}
bool writeSucceeded = f.close();
if (!okay || !writeSucceeded) {
LOG_ERROR("Can't write prefs!\n");
}
#else
LOG_ERROR("ERROR: Filesystem not implemented\n");
@@ -723,32 +744,32 @@ bool NodeDB::saveProto(const char *filename, size_t protoSize, const pb_msgdesc_
return okay;
}
void NodeDB::saveChannelsToDisk()
bool NodeDB::saveChannelsToDisk()
{
#ifdef FSCom
FSCom.mkdir("/prefs");
#endif
saveProto(channelFileName, meshtastic_ChannelFile_size, &meshtastic_ChannelFile_msg, &channelFile);
return saveProto(channelFileName, meshtastic_ChannelFile_size, &meshtastic_ChannelFile_msg, &channelFile);
}
void NodeDB::saveDeviceStateToDisk()
bool NodeDB::saveDeviceStateToDisk()
{
#ifdef FSCom
FSCom.mkdir("/prefs");
#endif
saveProto(prefFileName, sizeof(devicestate) + numMeshNodes * meshtastic_NodeInfoLite_size, &meshtastic_DeviceState_msg,
&devicestate);
// Note: if MAX_NUM_NODES=100 and meshtastic_NodeInfoLite_size=166, so will be approximately 17KB
// Because so huge we _must_ not use fullAtomic, because the filesystem is probably too small to hold two copies of this
return saveProto(prefFileName, sizeof(devicestate) + numMeshNodes * meshtastic_NodeInfoLite_size, &meshtastic_DeviceState_msg,
&devicestate, false);
}
void NodeDB::saveToDisk(int saveWhat)
bool NodeDB::saveToDiskNoRetry(int saveWhat)
{
bool success = true;
#ifdef FSCom
FSCom.mkdir("/prefs");
#endif
if (saveWhat & SEGMENT_DEVICESTATE) {
saveDeviceStateToDisk();
}
if (saveWhat & SEGMENT_CONFIG) {
config.has_device = true;
config.has_display = true;
@@ -758,7 +779,7 @@ void NodeDB::saveToDisk(int saveWhat)
config.has_network = true;
config.has_bluetooth = true;
saveProto(configFileName, meshtastic_LocalConfig_size, &meshtastic_LocalConfig_msg, &config);
success &= saveProto(configFileName, meshtastic_LocalConfig_size, &meshtastic_LocalConfig_msg, &config);
}
if (saveWhat & SEGMENT_MODULECONFIG) {
@@ -775,12 +796,45 @@ void NodeDB::saveToDisk(int saveWhat)
moduleConfig.has_audio = true;
moduleConfig.has_paxcounter = true;
saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig);
success &=
saveProto(moduleConfigFileName, meshtastic_LocalModuleConfig_size, &meshtastic_LocalModuleConfig_msg, &moduleConfig);
}
// We might need to rewrite the OEM data if we are reformatting the FS
if ((saveWhat & SEGMENT_OEM) && hasOemStore) {
success &= saveProto(oemConfigFile, meshtastic_OEMStore_size, &meshtastic_OEMStore_msg, &oemStore);
}
if (saveWhat & SEGMENT_CHANNELS) {
saveChannelsToDisk();
success &= saveChannelsToDisk();
}
if (saveWhat & SEGMENT_DEVICESTATE) {
success &= saveDeviceStateToDisk();
}
return success;
}
bool NodeDB::saveToDisk(int saveWhat)
{
bool success = saveToDiskNoRetry(saveWhat);
if (!success) {
LOG_ERROR("Failed to save to disk, retrying...\n");
#ifdef ARCH_NRF52 // @geeksville is not ready yet to say we should do this on other platforms. See bug #4184 discussion
FSCom.format();
// We need to rewrite the OEM data if we are reformatting the FS
saveWhat |= SEGMENT_OEM;
#endif
success = saveToDiskNoRetry(saveWhat);
RECORD_CRITICALERROR(success ? meshtastic_CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE
: meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE);
}
return success;
}
const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex)
@@ -991,7 +1045,8 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
if ((numMeshNodes >= MAX_NUM_NODES) || (memGet.getFreeHeap() < meshtastic_NodeInfoLite_size * 3)) {
if (screen)
screen->print("Warn: node database full!\nErasing oldest entry\n");
LOG_WARN("Node database full! Erasing oldest entry\n");
LOG_WARN("Node database full with %i nodes and %i bytes free! Erasing oldest entry\n", numMeshNodes,
memGet.getFreeHeap());
// look for oldest node and erase it
uint32_t oldest = UINT32_MAX;
int oldestIndex = -1;

View File

@@ -19,6 +19,7 @@ DeviceState versions used to be defined in the .proto file but really only this
#define SEGMENT_MODULECONFIG 2
#define SEGMENT_DEVICESTATE 4
#define SEGMENT_CHANNELS 8
#define SEGMENT_OEM 16
#define DEVICESTATE_CUR_VER 23
#define DEVICESTATE_MIN_VER 22
@@ -40,7 +41,7 @@ uint32_t sinceReceived(const meshtastic_MeshPacket *p);
enum LoadFileResult {
// Successfully opened the file
SUCCESS = 1,
LOAD_SUCCESS = 1,
// File does not exist
NOT_FOUND = 2,
// Device does not have a filesystem
@@ -72,8 +73,8 @@ class NodeDB
NodeDB();
/// write to flash
void saveToDisk(int saveWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS),
saveChannelsToDisk(), saveDeviceStateToDisk();
/// @return true if the save was successful
bool saveToDisk(int saveWhat = SEGMENT_CONFIG | SEGMENT_MODULECONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS);
/** Reinit radio config if needed, because either:
* a) sometimes a buggy android app might send us bogus settings or
@@ -126,11 +127,12 @@ class NodeDB
void initConfigIntervals(), initModuleConfigIntervals(), resetNodes(), removeNodeByNum(NodeNum nodeNum);
bool factoryReset();
bool factoryReset(bool eraseBleBonds = false);
LoadFileResult loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields,
void *dest_struct);
bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct);
bool saveProto(const char *filename, size_t protoSize, const pb_msgdesc_t *fields, const void *dest_struct,
bool fullAtomic = true);
void installRoleDefaults(meshtastic_Config_DeviceConfig_Role role);
@@ -181,6 +183,13 @@ class NodeDB
/// Reinit device state from scratch (not loading from disk)
void installDefaultDeviceState(), installDefaultChannels(), installDefaultConfig(), installDefaultModuleConfig();
/// write to flash
/// @return true if the save was successful
bool saveToDiskNoRetry(int saveWhat);
bool saveChannelsToDisk();
bool saveDeviceStateToDisk();
};
extern NodeDB *nodeDB;
@@ -204,9 +213,6 @@ extern NodeDB *nodeDB;
prefs.is_power_saving = True
*/
// Our delay functions check for this for times that should never expire
#define NODE_DELAY_FOREVER 0xffffffff
/// Sometimes we will have Position objects that only have a time, so check for
/// valid lat/lon
static inline bool hasValidPosition(const meshtastic_NodeInfoLite *n)

View File

@@ -41,8 +41,10 @@ void PhoneAPI::handleStartConfig()
// Must be before setting state (because state is how we know !connected)
if (!isConnected()) {
onConnectionChanged(true);
observe(&service.fromNumChanged);
observe(&service->fromNumChanged);
#ifdef FSCom
observe(&xModem.packetReady);
#endif
}
// even if we were already connected - restart our state machine
@@ -61,8 +63,10 @@ void PhoneAPI::close()
if (state != STATE_SEND_NOTHING) {
state = STATE_SEND_NOTHING;
unobserve(&service.fromNumChanged);
unobserve(&service->fromNumChanged);
#ifdef FSCom
unobserve(&xModem.packetReady);
#endif
releasePhonePacket(); // Don't leak phone packets on shutdown
releaseQueueStatusPhonePacket();
releaseMqttClientProxyPhonePacket();
@@ -110,7 +114,9 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
break;
case meshtastic_ToRadio_xmodemPacket_tag:
LOG_INFO("Got xmodem packet\n");
#ifdef FSCom
xModem.handlePacket(toRadioScratch.xmodemPacket);
#endif
break;
#if !MESHTASTIC_EXCLUDE_MQTT
case meshtastic_ToRadio_mqttClientProxyMessage_tag:
@@ -180,7 +186,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.my_info = myNodeInfo;
state = STATE_SEND_OWN_NODEINFO;
service.refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon.
service->refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon.
break;
case STATE_SEND_OWN_NODEINFO: {
@@ -437,7 +443,7 @@ void PhoneAPI::handleDisconnect()
void PhoneAPI::releasePhonePacket()
{
if (packetForPhone) {
service.releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore
service->releaseToPool(packetForPhone); // we just copied the bytes, so don't need this buffer anymore
packetForPhone = NULL;
}
}
@@ -445,7 +451,7 @@ void PhoneAPI::releasePhonePacket()
void PhoneAPI::releaseQueueStatusPhonePacket()
{
if (queueStatusPacketForPhone) {
service.releaseQueueStatusToPool(queueStatusPacketForPhone);
service->releaseQueueStatusToPool(queueStatusPacketForPhone);
queueStatusPacketForPhone = NULL;
}
}
@@ -453,7 +459,7 @@ void PhoneAPI::releaseQueueStatusPhonePacket()
void PhoneAPI::releaseMqttClientProxyPhonePacket()
{
if (mqttClientProxyMessageForPhone) {
service.releaseMqttClientProxyMessageToPool(mqttClientProxyMessageForPhone);
service->releaseMqttClientProxyMessageToPool(mqttClientProxyMessageForPhone);
mqttClientProxyMessageForPhone = NULL;
}
}
@@ -489,19 +495,21 @@ bool PhoneAPI::available()
return true; // Always say we have something, because we might need to advance our state machine
case STATE_SEND_PACKETS: {
if (!queueStatusPacketForPhone)
queueStatusPacketForPhone = service.getQueueStatusForPhone();
queueStatusPacketForPhone = service->getQueueStatusForPhone();
if (!mqttClientProxyMessageForPhone)
mqttClientProxyMessageForPhone = service.getMqttClientProxyMessageForPhone();
mqttClientProxyMessageForPhone = service->getMqttClientProxyMessageForPhone();
bool hasPacket = !!queueStatusPacketForPhone || !!mqttClientProxyMessageForPhone;
if (hasPacket)
return true;
#ifdef FSCom
if (xmodemPacketForPhone.control == meshtastic_XModem_Control_NUL)
xmodemPacketForPhone = xModem.getForPhone();
if (xmodemPacketForPhone.control != meshtastic_XModem_Control_NUL) {
xModem.resetForPhone();
return true;
}
#endif
#ifdef ARCH_ESP32
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
@@ -512,7 +520,7 @@ bool PhoneAPI::available()
#endif
if (!packetForPhone)
packetForPhone = service.getForPhone();
packetForPhone = service->getForPhone();
hasPacket = !!packetForPhone;
// LOG_DEBUG("available hasPacket=%d\n", hasPacket);
return hasPacket;
@@ -530,7 +538,7 @@ bool PhoneAPI::available()
bool PhoneAPI::handleToRadioPacket(meshtastic_MeshPacket &p)
{
printPacket("PACKET FROM PHONE", &p);
service.handleToRadio(p);
service->handleToRadio(p);
return true;
}

View File

@@ -38,7 +38,7 @@ template <class T> class ProtobufModule : protected SinglePortModule
/**
* Return a mesh packet which has been preinited with a particular protobuf data payload and port number.
* You can then send this packet (after customizing any of the payload fields you might need) with
* service.sendToMesh()
* service->sendToMesh()
*/
meshtastic_MeshPacket *allocDataProtobuf(const T &payload)
{

View File

@@ -16,7 +16,6 @@
// In theory up to 27 dBm is possible, but the modules installed in most radios can cope with a max of 20. So BIG WARNING
// if you set power to something higher than 17 or 20 you might fry your board.
#define POWER_DEFAULT 17 // How much power to use if the user hasn't set a power level
#ifdef RADIOMASTER_900_BANDIT_NANO
// Structure to hold DAC and DB values
typedef struct {

View File

@@ -177,9 +177,6 @@ The band is from 902 to 928 MHz. It mentions channel number and its respective c
separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts at 903.08 MHz center frequency.
*/
// 1kb was too small
#define RADIO_STACK_SIZE 4096
/**
* Calculate airtime per
* https://www.rs-online.com/designspark/rel-assets/ds-assets/uploads/knowledge-items/application-notes-for-the-internet-of-things/LoRa%20Design%20Guide.pdf
@@ -337,7 +334,7 @@ bool RadioInterface::init()
{
LOG_INFO("Starting meshradio init...\n");
configChangedObserver.observe(&service.configChanged);
configChangedObserver.observe(&service->configChanged);
preflightSleepObserver.observe(&preflightSleep);
notifyDeepSleepObserver.observe(&notifyDeepSleep);
@@ -588,4 +585,4 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p)
sendingPacket = p;
return p->encrypted.size + sizeof(PacketHeader);
}
}

View File

@@ -1,4 +1,5 @@
#include "ReliableRouter.h"
#include "Default.h"
#include "MeshModule.h"
#include "MeshTypes.h"
#include "configuration.h"
@@ -17,7 +18,7 @@ ErrorCode ReliableRouter::send(meshtastic_MeshPacket *p)
// message will rebroadcast. But asking for hop_limit 0 in that context means the client app has no preference on hop
// counts and we want this message to get through the whole mesh, so use the default.
if (p->hop_limit == 0) {
p->hop_limit = (config.lora.hop_limit >= HOP_MAX) ? HOP_MAX : config.lora.hop_limit;
p->hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
}
auto copy = packetPool.allocCopy(*p);

View File

@@ -7,18 +7,19 @@
#include "configuration.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "meshUtils.h"
#include "modules/RoutingModule.h"
#if !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h"
#endif
/**
* Router todo
*
* DONE: Implement basic interface and use it elsewhere in app
* Add naive flooding mixin (& drop duplicate rx broadcasts), add tools for sending broadcasts with incrementing sequence #s
* Add an optional adjacent node only 'send with ack' mixin. If we timeout waiting for the ack, call handleAckTimeout(packet)
*
**/
#include "Default.h"
#if ARCH_PORTDUINO
#include "platform/portduino/PortduinoGlue.h"
#endif
#if ENABLE_JSON_LOGGING || ARCH_PORTDUINO
#include "serialization/MeshPacketSerializer.h"
#endif
#include "../userPrefs.h"
#define MAX_RX_FROMRADIO \
4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big
@@ -119,7 +120,7 @@ meshtastic_MeshPacket *Router::allocForSending()
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // Assume payload is decoded at start.
p->from = nodeDB->getNodeNum();
p->to = NODENUM_BROADCAST;
p->hop_limit = (config.lora.hop_limit >= HOP_MAX) ? HOP_MAX : config.lora.hop_limit;
p->hop_limit = Default::getConfiguredOrDefaultHopLimit(config.lora.hop_limit);
p->id = generatePacketId();
p->rx_time =
getValidTime(RTCQualityFromNet); // Just in case we process the packet locally - make sure it has a valid timestamp
@@ -188,14 +189,6 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
}
}
void printBytes(const char *label, const uint8_t *p, size_t numbytes)
{
LOG_DEBUG("%s: ", label);
for (size_t i = 0; i < numbytes; i++)
LOG_DEBUG("%02x ", p[i]);
LOG_DEBUG("\n");
}
/**
* Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall.
@@ -356,6 +349,13 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
} */
printPacket("decoded message", p);
#if ENABLE_JSON_LOGGING
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
#elif ARCH_PORTDUINO
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) {
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerialize(p, false).c_str());
}
#endif
return true;
}
}
@@ -471,6 +471,20 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
cancelSending(p->from, p->id);
skipHandle = true;
}
#if EVENT_MODE
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
(p->decoded.portnum == meshtastic_PortNum_ATAK_FORWARDER || p->decoded.portnum == meshtastic_PortNum_ATAK_PLUGIN ||
p->decoded.portnum == meshtastic_PortNum_PAXCOUNTER_APP || p->decoded.portnum == meshtastic_PortNum_IP_TUNNEL_APP ||
p->decoded.portnum == meshtastic_PortNum_AUDIO_APP || p->decoded.portnum == meshtastic_PortNum_PRIVATE_APP ||
p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP ||
p->decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP ||
p->decoded.portnum == meshtastic_PortNum_REMOTE_HARDWARE_APP)) {
LOG_DEBUG("Ignoring packet on blacklisted portnum during event\n");
cancelSending(p->from, p->id);
skipHandle = true;
}
#endif
} else {
printPacket("packet decoding failed or skipped (no PSK?)", p);
}
@@ -491,19 +505,38 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
{
#if ENABLE_JSON_LOGGING
// Even ignored packets get logged in the trace
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str());
#elif ARCH_PORTDUINO
// Even ignored packets get logged in the trace
if (settingsStrings[traceFilename] != "" || settingsMap[logoutputlevel] == level_trace) {
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
LOG_TRACE("%s\n", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str());
}
#endif
// assert(radioConfig.has_preferences);
bool ignore = is_in_repeated(config.lora.ignore_incoming, p->from) || (config.lora.ignore_mqtt && p->via_mqtt);
if (is_in_repeated(config.lora.ignore_incoming, p->from)) {
LOG_DEBUG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from);
packetPool.release(p);
return;
}
if (ignore) {
LOG_DEBUG("Ignoring incoming message, 0x%x is in our ignore list or came via MQTT\n", p->from);
} else if (ignore |= shouldFilterReceived(p)) {
LOG_DEBUG("Incoming message was filtered 0x%x\n", p->from);
if (config.lora.ignore_mqtt && p->via_mqtt) {
LOG_DEBUG("Message came in via MQTT from 0x%x\n", p->from);
packetPool.release(p);
return;
}
if (shouldFilterReceived(p)) {
LOG_DEBUG("Incoming message was filtered from 0x%x\n", p->from);
packetPool.release(p);
return;
}
// Note: we avoid calling shouldFilterReceived if we are supposed to ignore certain nodes - because some overrides might
// cache/learn of the existence of nodes (i.e. FloodRouter) that they should not
if (!ignore)
handleReceived(p);
handleReceived(p);
packetPool.release(p);
}
}

View File

@@ -23,7 +23,7 @@ static const float tcxoVoltage = 1.7;
* Wio-E5 module ONLY transmits through RFO_HP
* Receive: PA4=1, PA5=0
* Transmit(high output power, SMPS mode): PA4=0, PA5=1 */
static const RADIOLIB_PIN_TYPE rfswitch_pins[3] = {PA4, PA5, RADIOLIB_NC};
static const RADIOLIB_PIN_TYPE rfswitch_pins[5] = {PA4, PA5, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
static const Module::RfSwitchMode_t rfswitch_table[4] = {
{STM32WLx::MODE_IDLE, {LOW, LOW}}, {STM32WLx::MODE_RX, {HIGH, LOW}}, {STM32WLx::MODE_TX_HP, {LOW, HIGH}}, END_OF_MODE_TABLE};

View File

@@ -26,7 +26,7 @@ class SinglePortModule : public MeshModule
/**
* Return a mesh packet which has been preinited as a data packet with a particular port number.
* You can then send this packet (after customizing any of the payload fields you might need) with
* service.sendToMesh()
* service->sendToMesh()
*/
meshtastic_MeshPacket *allocDataPacket()
{
@@ -36,4 +36,4 @@ class SinglePortModule : public MeshModule
return p;
}
};
};

View File

@@ -1096,7 +1096,7 @@ int decodeRepeat(const char *in, int len, char *out, int olen, int ol, int *bit_
return -1;
if (left <= 0)
return olen + 1;
if (dist >= strlen(cur_line->data))
if ((size_t)dist >= strlen(cur_line->data))
return -1;
memmove(out + ol, cur_line->data + dist, min_of(left, dict_len));
if (left < dict_len)
@@ -1289,7 +1289,7 @@ int unishox2_decompress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(c
if (usx_templates[idx] == NULL)
break;
size_t tlen = strlen(usx_templates[idx]);
if (rem > tlen)
if ((size_t)rem > tlen)
break;
rem = tlen - rem;
int eof = 0;

View File

@@ -168,6 +168,8 @@ typedef struct _meshtastic_AdminMessage {
bool begin_edit_settings;
/* Commits an open transaction for any edits made to config, module config, owner, and channel settings */
bool commit_edit_settings;
/* Tell the node to factory reset config everything; all device state and configuration will be returned to factory defaults and BLE bonds will be cleared. */
int32_t factory_reset_device;
/* Tell the node to reboot into the OTA Firmware in this many seconds (or <0 to cancel reboot)
Only Implemented for ESP32 Devices. This needs to be issued to send a new main firmware via bluetooth. */
int32_t reboot_ota_seconds;
@@ -178,8 +180,8 @@ typedef struct _meshtastic_AdminMessage {
int32_t reboot_seconds;
/* Tell the node to shutdown in this many seconds (or <0 to cancel shutdown) */
int32_t shutdown_seconds;
/* Tell the node to factory reset, all device settings will be returned to factory defaults. */
int32_t factory_reset;
/* Tell the node to factory reset config; all device state and configuration will be returned to factory defaults; BLE bonds will be preserved. */
int32_t factory_reset_config;
/* Tell the node to reset the nodedb. */
int32_t nodedb_reset;
};
@@ -254,11 +256,12 @@ extern "C" {
#define meshtastic_AdminMessage_remove_fixed_position_tag 42
#define meshtastic_AdminMessage_begin_edit_settings_tag 64
#define meshtastic_AdminMessage_commit_edit_settings_tag 65
#define meshtastic_AdminMessage_factory_reset_device_tag 94
#define meshtastic_AdminMessage_reboot_ota_seconds_tag 95
#define meshtastic_AdminMessage_exit_simulator_tag 96
#define meshtastic_AdminMessage_reboot_seconds_tag 97
#define meshtastic_AdminMessage_shutdown_seconds_tag 98
#define meshtastic_AdminMessage_factory_reset_tag 99
#define meshtastic_AdminMessage_factory_reset_config_tag 99
#define meshtastic_AdminMessage_nodedb_reset_tag 100
/* Struct field encoding specification for nanopb */
@@ -298,11 +301,12 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,set_fixed_position,set_fixed
X(a, STATIC, ONEOF, BOOL, (payload_variant,remove_fixed_position,remove_fixed_position), 42) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,begin_edit_settings,begin_edit_settings), 64) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,commit_edit_settings,commit_edit_settings), 65) \
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_device,factory_reset_device), 94) \
X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_ota_seconds,reboot_ota_seconds), 95) \
X(a, STATIC, ONEOF, BOOL, (payload_variant,exit_simulator,exit_simulator), 96) \
X(a, STATIC, ONEOF, INT32, (payload_variant,reboot_seconds,reboot_seconds), 97) \
X(a, STATIC, ONEOF, INT32, (payload_variant,shutdown_seconds,shutdown_seconds), 98) \
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset,factory_reset), 99) \
X(a, STATIC, ONEOF, INT32, (payload_variant,factory_reset_config,factory_reset_config), 99) \
X(a, STATIC, ONEOF, INT32, (payload_variant,nodedb_reset,nodedb_reset), 100)
#define meshtastic_AdminMessage_CALLBACK NULL
#define meshtastic_AdminMessage_DEFAULT NULL

View File

@@ -178,6 +178,15 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_HELTEC_MESH_NODE_T114 = 69,
/* Sensecap Indicator from Seeed Studio. ESP32-S3 device with TFT and RP2040 coprocessor */
meshtastic_HardwareModel_SENSECAP_INDICATOR = 70,
/* Seeed studio T1000-E tracker card. NRF52840 w/ LR1110 radio, GPS, button, buzzer, and sensors. */
meshtastic_HardwareModel_TRACKER_T1000_E = 71,
/* RAK3172 STM32WLE5 Module (https://store.rakwireless.com/products/wisduo-lpwan-module-rak3172) */
meshtastic_HardwareModel_RAK3172 = 72,
/* Seeed Studio Wio-E5 (either mini or Dev kit) using STM32WL chip. */
meshtastic_HardwareModel_WIO_E5 = 73,
/* RadioMaster 900 Bandit, https://www.radiomasterrc.com/products/bandit-expresslrs-rf-module
SSD1306 OLED and No GPS */
meshtastic_HardwareModel_RADIOMASTER_900_BANDIT = 74,
/* ------------------------------------------------------------------------------------------------------------------------------------------
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.
------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -226,7 +235,14 @@ typedef enum _meshtastic_CriticalErrorCode {
meshtastic_CriticalErrorCode_SX1262_FAILURE = 10,
/* A (likely software but possibly hardware) failure was detected while trying to send packets.
If this occurs on your board, please post in the forum so that we can ask you to collect some information to allow fixing this bug */
meshtastic_CriticalErrorCode_RADIO_SPI_BUG = 11
meshtastic_CriticalErrorCode_RADIO_SPI_BUG = 11,
/* Corruption was detected on the flash filesystem but we were able to repair things.
If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field. */
meshtastic_CriticalErrorCode_FLASH_CORRUPTION_RECOVERABLE = 12,
/* Corruption was detected on the flash filesystem but we were unable to repair things.
NOTE: Your node will probably need to be reconfigured the next time it reboots (it will lose the region code etc...)
If you see this failure in the field please post in the forum because we are interested in seeing if this is occurring in the field. */
meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE = 13
} meshtastic_CriticalErrorCode;
/* How the location was acquired: manual, onboard GPS, external (EUD) GPS */
@@ -929,8 +945,8 @@ extern "C" {
#define _meshtastic_Constants_ARRAYSIZE ((meshtastic_Constants)(meshtastic_Constants_DATA_PAYLOAD_LEN+1))
#define _meshtastic_CriticalErrorCode_MIN meshtastic_CriticalErrorCode_NONE
#define _meshtastic_CriticalErrorCode_MAX meshtastic_CriticalErrorCode_RADIO_SPI_BUG
#define _meshtastic_CriticalErrorCode_ARRAYSIZE ((meshtastic_CriticalErrorCode)(meshtastic_CriticalErrorCode_RADIO_SPI_BUG+1))
#define _meshtastic_CriticalErrorCode_MAX meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE
#define _meshtastic_CriticalErrorCode_ARRAYSIZE ((meshtastic_CriticalErrorCode)(meshtastic_CriticalErrorCode_FLASH_CORRUPTION_UNRECOVERABLE+1))
#define _meshtastic_Position_LocSource_MIN meshtastic_Position_LocSource_LOC_UNSET
#define _meshtastic_Position_LocSource_MAX meshtastic_Position_LocSource_LOC_EXTERNAL

View File

@@ -63,7 +63,13 @@ typedef enum _meshtastic_TelemetrySensorType {
/* DFRobot Lark Weather station (temperature, humidity, pressure, wind speed and direction) */
meshtastic_TelemetrySensorType_DFROBOT_LARK = 24,
/* NAU7802 Scale Chip or compatible */
meshtastic_TelemetrySensorType_NAU7802 = 25
meshtastic_TelemetrySensorType_NAU7802 = 25,
/* BMP3XX High accuracy temperature and pressure */
meshtastic_TelemetrySensorType_BMP3XX = 26,
/* ICM-20948 9-Axis digital motion processor */
meshtastic_TelemetrySensorType_ICM20948 = 27,
/* MAX17048 1S lipo battery sensor (voltage, state of charge, time to go) */
meshtastic_TelemetrySensorType_MAX17048 = 28
} meshtastic_TelemetrySensorType;
/* Struct definitions */
@@ -197,8 +203,8 @@ extern "C" {
/* Helper constants for enums */
#define _meshtastic_TelemetrySensorType_MIN meshtastic_TelemetrySensorType_SENSOR_UNSET
#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_NAU7802
#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_NAU7802+1))
#define _meshtastic_TelemetrySensorType_MAX meshtastic_TelemetrySensorType_MAX17048
#define _meshtastic_TelemetrySensorType_ARRAYSIZE ((meshtastic_TelemetrySensorType)(meshtastic_TelemetrySensorType_MAX17048+1))

View File

@@ -9,9 +9,9 @@
#if HAS_WIFI
#include "mesh/wifi/WiFiAPClient.h"
#endif
#include "mqtt/JSON.h"
#include "Led.h"
#include "power.h"
#include "sleep.h"
#include "serialization/JSON.h"
#include <FSCommon.h>
#include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp>
@@ -161,7 +161,7 @@ void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
res->setHeader("Content-Type", "application/x-protobuf");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/mesh.proto");
res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto");
uint8_t txBuf[MAX_STREAM_BUF_SIZE];
uint32_t len = 1;
@@ -205,7 +205,7 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
res->setHeader("Access-Control-Allow-Headers", "Content-Type");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "PUT, OPTIONS");
res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/mesh.proto");
res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto");
if (req->getMethod() == "OPTIONS") {
res->setStatusCode(204); // Success with no content
@@ -798,9 +798,9 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
if (blink_target == "LED") {
uint8_t count = 10;
while (count > 0) {
setLed(true);
ledBlink.set(true);
delay(50);
setLed(false);
ledBlink.set(false);
delay(50);
count = count - 1;
}

View File

@@ -1,6 +1,7 @@
#include "mesh-pb-constants.h"
#include "FSCommon.h"
#include "configuration.h"
#include "FSCommon.h"
#include "mesh-pb-constants.h"
#include <Arduino.h>
#include <assert.h>
#include <pb_decode.h>
@@ -15,6 +16,7 @@ size_t pb_encode_to_bytes(uint8_t *destbuf, size_t destbufsize, const pb_msgdesc
LOG_ERROR("Panic: can't encode protobuf reason='%s'\n", PB_GET_ERROR(&stream));
assert(
0); // If this assert fails it probably means you made a field too large for the max limits specified in mesh.options
return 0;
} else {
return stream.bytes_written;
}
@@ -56,7 +58,7 @@ bool readcb(pb_istream_t *stream, uint8_t *buf, size_t count)
/// Write to an arduino file
bool writecb(pb_ostream_t *stream, const uint8_t *buf, size_t count)
{
File *file = (File *)stream->state;
auto file = (Print *)stream->state;
// LOG_DEBUG("writing %d bytes to protobuf file\n", count);
return file->write(buf, count) == count;
}
@@ -69,4 +71,4 @@ bool is_in_helper(uint32_t n, const uint32_t *array, pb_size_t count)
return true;
return false;
}
}

View File

@@ -14,7 +14,9 @@
/// max number of packets which can be waiting for delivery to android - note, this value comes from mesh.options protobuf
// FIXME - max_count is actually 32 but we save/load this as one long string of preencoded MeshPacket bytes - not a big array in
// RAM #define MAX_RX_TOPHONE (member_size(DeviceState, receive_queue) / member_size(DeviceState, receive_queue[0]))
#ifndef MAX_RX_TOPHONE
#define MAX_RX_TOPHONE 32
#endif
/// max number of nodes allowed in the mesh
#ifndef MAX_NUM_NODES

View File

@@ -230,7 +230,7 @@ int handleAPIv1ToRadio(const struct _u_request *req, struct _u_response *res, vo
ulfius_add_header_to_response(res, "Access-Control-Allow-Origin", "*");
ulfius_add_header_to_response(res, "Access-Control-Allow-Methods", "PUT, OPTIONS");
ulfius_add_header_to_response(res, "X-Protobuf-Schema",
"https://raw.githubusercontent.com/meshtastic/protobufs/master/mesh.proto");
"https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto");
if (req->http_verb == "OPTIONS") {
ulfius_set_response_properties(res, U_OPT_STATUS, 204);
@@ -267,7 +267,7 @@ int handleAPIv1FromRadio(const struct _u_request *req, struct _u_response *res,
ulfius_add_header_to_response(res, "Access-Control-Allow-Origin", "*");
ulfius_add_header_to_response(res, "Access-Control-Allow-Methods", "GET");
ulfius_add_header_to_response(res, "X-Protobuf-Schema",
"https://raw.githubusercontent.com/meshtastic/protobufs/master/mesh.proto");
"https://raw.githubusercontent.com/meshtastic/protobufs/master/meshtastic/mesh.proto");
uint8_t txBuf[MAX_STREAM_BUF_SIZE];
uint32_t len = 1;

View File

@@ -56,4 +56,12 @@ char *strnstr(const char *s, const char *find, size_t slen)
s--;
}
return ((char *)s);
}
void printBytes(const char *label, const uint8_t *p, size_t numbytes)
{
LOG_DEBUG("%s: ", label);
for (size_t i = 0; i < numbytes; i++)
LOG_DEBUG("%02x ", p[i]);
LOG_DEBUG("\n");
}

View File

@@ -1,4 +1,6 @@
#pragma once
#include "DebugConfiguration.h"
#include <stdint.h>
/// C++ v17+ clamp function, limits a given value to a range defined by lo and hi
template <class T> constexpr const T &clamp(const T &v, const T &lo, const T &hi)

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