Compare commits

..

124 Commits

Author SHA1 Message Date
Jm Casler
ea40bd991c Merge pull request #660 from mc-hamster/master
Update version.properties to 1.1.33
2021-01-25 17:12:32 -08:00
Jm Casler
e19dd46f0f Merge branch 'master' into master 2021-01-25 17:05:52 -08:00
Jm
532b06c280 Update version.properties to 1.1.33 2021-01-25 17:01:47 -08:00
Jm Casler
0cf7aaffff Merge pull request #658 from sachaw/master
Another missing header
2021-01-24 18:06:14 -08:00
Sacha Weatherstone
e2e1819ef1 Merge branch 'master' of https://github.com/sachaw/meshtastic-device 2021-01-24 10:49:23 +11:00
Sacha Weatherstone
31b89e2932 Another header required 2021-01-24 10:48:48 +11:00
Jm Casler
a021ff7eb8 Merge pull request #656 from sachaw/master
fix cors for API requsts & fix spiffs url
2021-01-22 23:36:27 -08:00
Jm Casler
bb5d0fac90 Merge branch 'master' into master 2021-01-22 23:26:53 -08:00
Jm Casler
df5ed64514 Merge pull request #655 from mc-hamster/master
Fix for #650 - build-all.sh will fail
2021-01-22 23:26:39 -08:00
Sacha Weatherstone
9db5f9ff67 fix cors for API requsts & fix spiffs url 2021-01-23 17:42:15 +11:00
Jm
ca83a78e13 Fix for #650 - build-all.sh will fail 2021-01-22 19:50:12 -08:00
Jm Casler
13eef9a309 Merge pull request #653 from mc-hamster/master
Update comments in SerialPlugin
2021-01-20 21:42:12 -08:00
Jm Casler
2a8ac2c0c6 Merge branch 'master' into master 2021-01-20 19:04:46 -08:00
Jm Casler
c97342db99 Merge pull request #63 from mc-hamster/serial
Update comments on SerialPlugin
2021-01-20 19:03:44 -08:00
Jm Casler
d7b2a0ed79 Merge pull request #62 from mc-hamster/master
update serial from master
2021-01-20 19:02:58 -08:00
Jm
af0a1b5db5 Update comments of SerialPlugin 2021-01-20 19:02:08 -08:00
Jm Casler
9cf030d587 Merge pull request #652 from mc-hamster/master
#649 More webserver refactoring
2021-01-19 21:52:56 -08:00
Jm
c04d70d5e5 Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-01-19 21:38:30 -08:00
Jm
2a47819fd6 #649 More webserver refactoring 2021-01-19 21:38:17 -08:00
Jm Casler
4516c8f9b5 Merge pull request #61 from meshtastic/master
Merge pull request #648 from mc-hamster/master
2021-01-19 21:27:24 -08:00
Jm
e4fdf26dc7 #649 - First pass on the refactoring of the webserver 2021-01-19 21:26:23 -08:00
Jm Casler
39d14fedc2 Merge pull request #648 from mc-hamster/master
#647 - Fix for admin mode being forced on boards without hardware pullup
2021-01-18 10:56:13 -08:00
Jm Casler
1da38fc748 Merge branch 'master' into master 2021-01-18 10:44:38 -08:00
Jm
b5f50efdcd #647 - Fix for admin mode being forced on boards without hardware pullup 2021-01-18 10:43:15 -08:00
Jm Casler
046e691d4e Merge pull request #645 from mc-hamster/master
#635 Added web_request_count and fixed printf of psram
2021-01-17 17:26:56 -08:00
Jm Casler
e72531b090 Merge pull request #60 from mc-hamster/master
Update Serial
2021-01-17 15:41:21 -08:00
Jm
81e320c9cf Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-01-17 15:40:27 -08:00
Jm
fa8cc74141 Update to Serial Plugin to make it easy to override the device configuration 2021-01-17 15:40:25 -08:00
Jm Casler
c7d9ff7cc0 Merge branch 'master' into master 2021-01-17 10:36:54 -08:00
Jm
8704a9d08f Merge branch 'master' of https://github.com/mc-hamster/Meshtastic-device 2021-01-17 10:30:40 -08:00
Jm
c0d27e2ce9 #635 Added web_request_count and fixed printf of psram 2021-01-17 10:30:34 -08:00
Jm Casler
84b9028ecb Merge pull request #642 from mc-hamster/master
#635 - Added memory usage statistics
2021-01-17 04:52:03 -08:00
Jm Casler
4fda7098c0 Merge branch 'master' into master 2021-01-17 04:44:42 -08:00
Jm
8e8264efb0 #635 - Added memory usage statistics 2021-01-17 00:29:29 -08:00
Jm Casler
54e780a6ca Merge pull request #641 from mc-hamster/master
Update generated protobuf; Update serial plugin; #615 - Configure charge current
2021-01-17 00:18:33 -08:00
Jm
125eb2b784 Fix for build fail on NRF 2021-01-17 00:11:26 -08:00
Jm Casler
6ea9cdc83b Merge pull request #59 from mc-hamster/master
Update serial plugin with new protobufs
2021-01-17 00:03:13 -08:00
Jm
c0711fde69 #615 Allow rate for tbeam battery charger to be configurable 2021-01-16 23:10:08 -08:00
Jm
20b8d2c4a5 Set the port to PortNum_SERIAL_APP 2021-01-16 22:41:33 -08:00
Jm
73ae151971 Added serialplugin_timeout to SerialPlugin 2021-01-16 22:39:28 -08:00
Jm Casler
f4806c9dd7 Merge branch 'serial' into master 2021-01-16 22:35:56 -08:00
Jm
79532210e8 SerialPlugin - Update comments with better instructions 2021-01-16 22:31:56 -08:00
Jm
d7f26493a5 Update to SerialPlugin to take advantage of the configs 2021-01-16 22:27:33 -08:00
Jm
b9d025dd58 Missed adding the proto file 2021-01-16 20:53:45 -08:00
Jm
f435086a5a Update generated protobufs 2021-01-16 20:50:58 -08:00
Jm
3dcdf372d7 add remote update 2021-01-16 20:12:31 -08:00
Jm
cd84f2867c Updated submodule proto 2021-01-16 20:00:37 -08:00
Jm
cafe00e463 Update of serialplugin 2021-01-16 19:40:47 -08:00
Jm
6da4e30215 Merge branch 'serial' of https://github.com/mc-hamster/Meshtastic-device into serial 2021-01-14 22:38:53 -08:00
Jm
dbf0569e29 Update comments for serial pins recommended by @ryguy 2021-01-14 22:36:51 -08:00
Jm Casler
18220b88b3 Merge pull request #58 from mc-hamster/master
update serial from master
2021-01-14 22:36:17 -08:00
Jm Casler
665da2fb00 Merge pull request #57 from meshtastic/master
Merge pull request #633 from mc-hamster/master
2021-01-14 22:34:01 -08:00
Jm Casler
57ffe6622d Merge pull request #56 from mc-hamster/master
update serial from master
2021-01-14 22:25:20 -08:00
Jm Casler
485fec9649 Merge pull request #633 from mc-hamster/master
Fix for #632 (move web server to osthread) and #611 (move airtime to osthread)
2021-01-14 22:06:18 -08:00
Jm
bd85736226 SerialPlugin - Exclude working on non esp32 platforms. 2021-01-14 21:59:26 -08:00
Jm
4ec8986934 Fixed switching to uint32 from 16 for airtime in json report 2021-01-14 21:45:41 -08:00
Jm Casler
b963216764 Merge pull request #55 from mc-hamster/serial
Update for SerialPlugin and Airtime
2021-01-14 21:43:28 -08:00
Jm Casler
813fd95bc8 Merge pull request #54 from geeksville/mc-master
fix @mc-hamster build to work on linux/nrf52
2021-01-14 21:33:27 -08:00
Kevin Hester
3598c91c29 fix @mc-hamster build to work on linux/nrf52 2021-01-15 13:30:40 +08:00
Jm
507cd1dd20 #639 - Move from counting seconds to milliseconds 2021-01-14 18:40:18 -08:00
Jm
e39506824d Added more comments 2021-01-14 18:08:23 -08:00
Jm
f68a31ab28 serialplugin - ability to configure ACK 2021-01-13 23:21:55 -08:00
Jm
b1181deb58 serialplugin - Added my todo list 2021-01-13 23:02:13 -08:00
Jm
89b32dd7ee Fix comments in serial plugin 2021-01-13 22:51:36 -08:00
Jm
c54e87f9a2 Update SerialPlugin.cpp
Added documentation.
2021-01-13 22:50:02 -08:00
Jm
eee7e1de57 Update to serial plugin 2021-01-13 22:39:11 -08:00
Jm
3c60df1565 Partial work on the SerialPlugin 2021-01-13 20:22:59 -08:00
Jm Casler
a827017bd2 Merge pull request #53 from crossan007/master
add power statistics for #635
2021-01-11 17:54:27 -08:00
Charles Crossan
95c502c658 fix typo 2021-01-11 20:30:02 -05:00
Charles Crossan
0f573901d5 didn't need power.h 2021-01-11 20:28:09 -05:00
Charles Crossan
fdc9bf5783 add power statistics for #635 2021-01-11 20:25:02 -05:00
Jm Casler
37e0f9a325 Merge pull request #52 from mc-hamster/osthread
maybe this will fix the linux build?
2021-01-09 19:32:40 -08:00
Jm
0c06d8db3c maybe this will fix the linux build? 2021-01-09 19:31:16 -08:00
Jm Casler
0be4bbb369 Merge pull request #51 from mc-hamster/osthread
Does this fix the linux build?
2021-01-09 19:17:45 -08:00
Jm
f02ab88393 Does this fix the linux build? 2021-01-09 19:15:10 -08:00
Jm Casler
c9d4de8808 Merge pull request #50 from mc-hamster/osthread
fixed in linux now?
2021-01-09 19:07:16 -08:00
Jm
adb912b665 fixed in linux now? 2021-01-09 19:06:34 -08:00
Jm Casler
3f5da1e03e Merge pull request #49 from mc-hamster/osthread
another linux fix?
2021-01-09 18:37:04 -08:00
Jm
0a40d920e3 Update BluetoothUtil.cpp
yet another linux fix?
2021-01-09 18:36:23 -08:00
Jm Casler
39311f1e40 Merge pull request #48 from mc-hamster/osthread
is linux fixed now?
2021-01-09 18:27:32 -08:00
Jm
9cd24a5646 is linux fixed now? 2021-01-09 18:27:03 -08:00
Jm Casler
1c0efde315 Merge pull request #47 from mc-hamster/osthread
is linux fixed now?
2021-01-09 18:09:57 -08:00
Jm
c82905bbdd is linux fixed now? 2021-01-09 18:09:16 -08:00
Jm Casler
275eace968 Merge pull request #46 from mc-hamster/osthread
more linux fixes
2021-01-09 17:52:09 -08:00
Jm
5688c8b81e more linux build fixes 2021-01-09 17:50:58 -08:00
Jm
8b2798abd5 Added a few commented out default_envs 2021-01-09 17:44:20 -08:00
Jm Casler
6d977923b6 Merge pull request #45 from mc-hamster/osthread
more fixes for linux build
2021-01-09 17:34:34 -08:00
Jm
52dacaed37 more fixes for linux build 2021-01-09 17:30:36 -08:00
Jm Casler
7a381eaea1 Merge pull request #44 from mc-hamster/osthread
Fix for breaking the linux build
2021-01-09 13:47:43 -08:00
Jm
69391e186b Fix for breaking the linux build 2021-01-09 13:47:10 -08:00
Jm Casler
06f8beaa17 Merge pull request #43 from mc-hamster/osthread
fix for building on NRF platform.
2021-01-08 23:00:36 -08:00
Jm
3798f4ca5b fix for building on NRF platform. 2021-01-08 22:59:37 -08:00
Jm Casler
4fd243a6e4 Merge pull request #42 from mc-hamster/osthread
Moving web server and airtime to osthread model. Moved web server to mesh/wifi
2021-01-08 22:29:45 -08:00
Jm
d458f673be Web server is now treaded and moved to mesh/wifi/* 2021-01-08 22:25:44 -08:00
Jm
cfcb00b943 that's enough for tonight. web server is in its own thread, needs to be further optimized but it works enough. next is to refactor. 2021-01-08 20:43:51 -08:00
Jm
977e47d109 partial work 2021-01-08 20:06:11 -08:00
Jm Casler
cfeb40f36d Merge pull request #41 from mc-hamster/master
Updated osthread branch from master
2021-01-08 19:57:19 -08:00
Jm Casler
4fcc3ac1de Merge pull request #40 from meshtastic/master
Updated my master from head
2021-01-08 19:54:57 -08:00
Kevin Hester
f4afa6931b Merge pull request #630 from geeksville/dev
Dev
2021-01-08 13:38:23 +08:00
Kevin Hester
71be71d63d add note about how to send messages thanks @rw-w for the question 2021-01-08 13:31:28 +08:00
Kevin Hester
de9f7e6c39 update protos 2021-01-08 13:21:14 +08:00
Kevin Hester
7c8db2b501 Merge remote-tracking branch 'root/master' into dev
# Conflicts:
#	docs/software/plugin-api.md
2021-01-08 13:20:28 +08:00
Kevin Hester
cd653f9434 Merge pull request #629 from ry-w/patch-1
Update plugin-api.md
2021-01-08 13:19:03 +08:00
Kevin Hester
74bc05936d Merge remote-tracking branch 'root/master' into dev 2021-01-08 13:16:27 +08:00
Kevin Hester
7aacfd66ef add assertIsSetup() and use it from OSThread constructor
fixes nasty bug @mc-hamster discovered with plugin order of operations
2021-01-08 13:15:49 +08:00
Kevin Hester
3636b87db0 formatting 2021-01-08 11:52:43 +08:00
ry-w
d6bd328576 Update plugin-api.md
Couple of clarifications, and fixes to minor errors.
2021-01-06 14:24:51 -08:00
Jm
0af5b225c4 intermediate 2021-01-05 23:32:33 -08:00
Jm
f7dcef39ce intermediate work 2021-01-05 23:21:14 -08:00
Kevin Hester
07042178d2 Merge pull request #627 from mc-hamster/master
Include instrusctions for real time backtrace decoding.
2021-01-06 14:55:46 +08:00
Kevin Hester
243878f2a0 Merge branch 'master' into master 2021-01-06 14:39:57 +08:00
Kevin Hester
d3f8a76cce Merge pull request #625 from geeksville/dev
fix #624 - update battery level and current time on mynodeinfo
2021-01-06 14:39:42 +08:00
Kevin Hester
20131a51a2 Merge branch 'master' into dev 2021-01-06 14:31:11 +08:00
Kevin Hester
1c9a369774 Merge branch 'master' into master 2021-01-06 13:26:30 +08:00
Kevin Hester
dcb426f58f Merge pull request #628 from IZ1IVA/patch-3
Update radio-settings.md
2021-01-06 13:23:53 +08:00
Jm Casler
35bcb5297a Merge pull request #38 from mc-hamster/master
update from master to osthread
2021-01-05 19:04:15 -08:00
Jm
84e3d7c276 Test fixes for Tobiases 2021-01-05 18:57:03 -08:00
IZ1IVA
9b03f0ac8e Update radio-settings.md
Fixed frequency table non showing
2021-01-05 12:13:18 +01:00
Jm Casler
eb402809e2 Merge branch 'master' into master 2021-01-04 22:16:07 -08:00
Jm
e9c9e40624 Include instrusctions for real time backtrace decoding. 2021-01-04 22:15:19 -08:00
Kevin Hester
01eed97b91 Merge branch 'master' into dev 2021-01-04 10:00:38 +08:00
Kevin Hester
94a47dba7d fix #624 - update battery level and current time on mynodeinfo 2021-01-04 09:59:53 +08:00
Jm
8295b88d96 Checking in work so I don't lose it. Nothing's broke with the build. 2021-01-01 21:20:34 -08:00
Jm
925829dc58 Partial work to migrate to OSThread model 2021-01-01 12:31:46 -08:00
50 changed files with 1338 additions and 682 deletions

View File

@@ -14,7 +14,7 @@ BOARDS_ESP32="tlora-v2 tlora-v1 tlora-v2-1-1.6 tbeam heltec tbeam0.7"
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
BOARDS_NRF52="lora-relay-v1"
NUM_JOBS=2
NUM_JOBS=2 || true
OUTDIR=release/latest

View File

@@ -2,7 +2,7 @@
We use the same channel maps as LoRaWAN (though this is not LoRaWAN).
![freq table](images/LoRa-Frequency-Bands.jpg)
![freq table](/images/LoRa-Frequency-Bands.jpg)
See [this site](https://www.rfwireless-world.com/Tutorials/LoRa-channels-list.html) for more information.

View File

@@ -39,9 +39,20 @@ cd Meshtastic-device
## Decoding stack traces
### Option 1
If you get a crash, you can decode the addresses from the `Backtrace:` line:
1. Save the `Backtrace: 0x....` line to a file, e.g., `backtrace.txt`.
2. Run `bin/exception_decoder.py backtrace.txt` (this uses symbols from the
last `firmware.elf`, so you must be running the same binary that's still in
your `.pio/build` directory).
### Option 2
You can run the exception decoder to monitor the serial output and decode backtraces in real time.
1. From within PlatformIO, open a new terminal.
2. At the the terminal, enter:
`pio device monitor --port /dev/cu.SLAB_USBtoUART -f esp32_exception_decoder`
Replace the value of port with the location of your serial port.

View File

@@ -15,10 +15,10 @@ Packets can be sent/received either as raw binary structures or as [Protobufs](h
The relevant bits of the class heirarchy are as follows
* [MeshPlugin](/src/mesh/MeshPlugin.h) (in src/mesh/MeshPlugin.h) - you probably don't want to use this baseclass directly
* [SinglePortPlugin](/src/mesh/SinglePortPlugin.h) (in src/mesh/SinglePortPlugin.h) - for plugins that receive from a single port number (the normal case)
* [ProtobufPlugin](/src/mesh/ProtobufPlugin.h) (in src/mesh/ProtobufPlugin.h) - for plugins that are sending/receiving a single particular Protobuf type. Inherit from this if you are using protocol buffers in your plugin.
* [SinglePortPlugin](/src/mesh/SinglePortPlugin.h) (in src/mesh/SinglePortPlugin.h) - for plugins that send/receive from a single port number (the normal case)
* [ProtobufPlugin](/src/mesh/ProtobufPlugin.h) (in src/mesh/ProtobufPlugin.h) - for plugins that send/receive a single particular Protobuf type. Inherit from this if you are using protocol buffers in your plugin.
You will typically want to inherit from either SinglePortPlugin (if you are just sending raw bytes) or ProtobufPlugin (if you are sending protobufs). You'll implement your own handleReceived/handleReceivedProtobuf - probably based on the example code.
You will typically want to inherit from either SinglePortPlugin (if you are just sending/receiving raw bytes) or ProtobufPlugin (if you are sending/receiving protobufs). You'll implement your own handleReceived/handleReceivedProtobuf - probably based on the example code.
If your plugin needs to perform any operations at startup you can override and implement the setup() method to run your code.
@@ -45,25 +45,30 @@ A number of [key services](/src/plugins) are implemented using the plugin API, t
The easiest way to get started is:
* [Build and install](build-instructions.md) the standard codebase from github.
* Copy [src/plugins/ReplyPlugin.*](/src/plugins/ReplyPlugin.cpp) into src/plugins/YourPlugin.*. Then change the port number from REPLY_APP to PRIVATE_APP.
* Copy [src/plugins/ReplyPlugin.*](/src/plugins/ReplyPlugin.cpp) into src/plugins/YourPlugin.*. Then change the port number from *PortNum_REPLY_APP* to *PortNum_PRIVATE_APP*.
* Edit plugins/Plugins.cpp:setupPlugins() to add a call to create an instance of your plugin (see comment at head of that function)
* Rebuild with your new messaging goodness and install on the device
* Use the [meshtastic commandline tool](https://github.com/meshtastic/Meshtastic-python) to send a packet to your board "meshtastic --dest 1234 --ping"
* Use the [meshtastic commandline tool](https://github.com/meshtastic/Meshtastic-python) to send a packet to your board, for example "*meshtastic --dest 1234 --sendping*", where *1234* is another mesh node to send the ping to.
## Threading
It is very common that you would like your plugin to be invoked periodically.
We use a crude/basic cooperative threading system to allow this on any of our supported platforms. Simply inherit from OSThread and implement runOnce(). See the OSThread [documentation](/src/concurrency/OSThread.h) for more details. For an example consumer of this API see RemoteHardwarePlugin::runOnce.
## Sending messages
If you would like to proactively send messages (rather than just responding to them), just call service.sendToMesh(). For an example of this see [NodeInfoPlugin::sendOurNodeInfo(...)](/src/plugins/NodeInfoPlugin.cpp).
## Picking a port number
For any new 'apps' that run on the device or via sister apps on phones/PCs they should pick and use a unique 'portnum' for their application.
If you are making a new app using meshtastic, please send in a pull request to add your 'portnum' to [the master list](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/portnums.proto). PortNums should be assigned in the following range:
* 0-63 Core Meshtastic use, do not use for third party apps
* 64-127 Registered 3rd party apps, send in a pull request that adds a new entry to portnums.proto to register your application
* 256-511 Use one of these portnums for your private applications that you don't want to register publically
* 1024-66559 Are reserved for use by IP tunneling (see FIXME for more information)
* **0-63** Core Meshtastic use; do not use for third party apps
* **64-127** Registered 3rd party apps. Send in a pull request that adds a new entry to portnums.proto to register your application
* **256-511** Use one of these portnums for your private applications that you don't want to register publically
* **1024-66559** Are reserved for use by IP tunneling (see *FIXME* for more information)
All other values are reserved.
@@ -73,4 +78,4 @@ If you would like to use protocol buffers to define the structures you send over
* Create a new .proto file in the protos directory. You can use the existing [remote_hardware.proto](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/remote_hardware.proto) file as an example.
* Run "bin/regen-protos.sh" to regenerate the C code for accessing the protocol buffers. If you don't have the required nanopb tool, follow the instructions printed by the script to get it.
* Done! You can now use your new protobuf just like any of the existing protobufs in meshtastic.
* Done! You can now use your new protobuf just like any of the existing protobufs in meshtastic.

View File

@@ -9,8 +9,13 @@
; https://docs.platformio.org/page/projectconf.html
[platformio]
;default_envs = tbeam
;default_envs = tbeam0.7
;default_envs = heltec
;default_envs = tlora-v1
;default_envs = tlora-v2
;default_envs = lora-relay-v1 # nrf board
default_envs = linux # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
;default_envs = heltec # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
[common]
; common is not currently used
@@ -180,10 +185,10 @@ build_type = debug ; I'm debugging with ICE a lot now
build_flags =
${arduino_base.build_flags} -Wno-unused-variable
-Isrc/nrf52
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.3
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.7
;-DCFG_DEBUG=3
src_filter =
${arduino_base.src_filter} -<esp32/> -<nimble/> -<meshwifi/> -<mesh/wifi/>
${arduino_base.src_filter} -<esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/>
lib_ignore =
BluetoothOTA
monitor_port = /dev/ttyACM1
@@ -310,7 +315,7 @@ lib_deps =
; The Portduino based sim environment on top of linux
[env:linux]
platform = https://github.com/geeksville/platform-portduino.git
src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/> -<meshwifi/>
src_filter = ${env.src_filter} -<esp32/> -<nimble/> -<nrf52/> -<mesh/http/>
build_flags = ${arduino_base.build_flags} -O0
framework = arduino
board = linux_x86_64

2
proto

Submodule proto updated: dfe7bc1217...855da8701e

View File

@@ -1,4 +1,5 @@
#include "power.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "main.h"
#include "sleep.h"
@@ -268,9 +269,42 @@ bool Power::axp192Init()
DEBUG_MSG("DCDC3: %s\n", axp.isDCDC3Enable() ? "ENABLE" : "DISABLE");
DEBUG_MSG("Exten: %s\n", axp.isExtenEnable() ? "ENABLE" : "DISABLE");
//axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA); // actual limit (in HW) on the tbeam is 450mA
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA); // There's no HW limit on the tbeam. Setting to 450mz to be a good neighbor on the usb bus.
if (radioConfig.preferences.charge_current == ChargeCurrent_MAUnset) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA100) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_100MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA190) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_190MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA280) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_280MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA360) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_360MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA450) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_450MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA550) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_550MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA630) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_630MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA700) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_700MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA780) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_780MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA880) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_880MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA960) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_960MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1000) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1000MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1080) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1080MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1160) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1160MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1240) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1240MA);
} else if (radioConfig.preferences.charge_current == ChargeCurrent_MA1320) {
axp.setChargeControlCur(AXP1XX_CHARGE_CUR_1320MA);
}
#if 0
// Not connected

View File

@@ -11,7 +11,7 @@ namespace meshtastic
*/
enum OptionalBool { OptFalse = 0, OptTrue = 1, OptUnknown = 2 };
/// Describes the state of the GPS system.
/// Describes the state of the Power system.
class PowerStatus : public Status
{

View File

@@ -3,33 +3,35 @@
#define periodsToLog 48
// A reminder that there are 3600 seconds in an hour so I don't have
// to keep googling it.
// This can be changed to a smaller number to speed up testing.
//
AirTime *airTime;
uint32_t secondsPerPeriod = 3600;
uint32_t lastMillis = 0;
uint32_t secSinceBoot = 0;
// AirTime at;
// Don't read out of this directly. Use the helper functions.
struct airtimeStruct {
uint16_t periodTX[periodsToLog];
uint16_t periodRX[periodsToLog];
uint16_t periodRX_ALL[periodsToLog];
uint32_t periodTX[periodsToLog]; // AirTime transmitted
uint32_t periodRX[periodsToLog]; // AirTime received and repeated (Only valid mesh packets)
uint32_t periodRX_ALL[periodsToLog]; // AirTime received regardless of valid mesh packet. Could include noise.
uint8_t lastPeriodIndex;
} airtimes;
void logAirtime(reportTypes reportType, uint32_t airtime_ms)
void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms)
{
if (reportType == TX_LOG) {
airtimes.periodTX[0] = airtimes.periodTX[0] + round(airtime_ms / 1000);
DEBUG_MSG("AirTime - Packet transmitted : %ums\n", airtime_ms);
airtimes.periodTX[0] = airtimes.periodTX[0] + airtime_ms;
} else if (reportType == RX_LOG) {
airtimes.periodRX[0] = airtimes.periodRX[0] + round(airtime_ms / 1000);
DEBUG_MSG("AirTime - Packet received : %ums\n", airtime_ms);
airtimes.periodRX[0] = airtimes.periodRX[0] + airtime_ms;
} else if (reportType == RX_ALL_LOG) {
airtimes.periodRX_ALL[0] = airtimes.periodRX_ALL[0] + round(airtime_ms / 1000);
DEBUG_MSG("AirTime - Packet received (noise?) : %ums\n", airtime_ms);
airtimes.periodRX_ALL[0] = airtimes.periodRX_ALL[0] + airtime_ms;
} else {
// Unknown report type
DEBUG_MSG("AirTime - Unknown report time. This should never happen!!\n");
}
}
@@ -38,29 +40,27 @@ uint8_t currentPeriodIndex()
return ((getSecondsSinceBoot() / secondsPerPeriod) % periodsToLog);
}
void airtimeCalculator()
void airtimeRotatePeriod()
{
if (millis() - lastMillis > 1000) {
lastMillis = millis();
secSinceBoot++;
if (airtimes.lastPeriodIndex != currentPeriodIndex()) {
for (int i = periodsToLog - 2; i >= 0; --i) {
airtimes.periodTX[i + 1] = airtimes.periodTX[i];
airtimes.periodRX[i + 1] = airtimes.periodRX[i];
airtimes.periodRX_ALL[i + 1] = airtimes.periodRX_ALL[i];
}
airtimes.periodTX[0] = 0;
airtimes.periodRX[0] = 0;
airtimes.periodRX_ALL[0] = 0;
airtimes.lastPeriodIndex = currentPeriodIndex();
if (airtimes.lastPeriodIndex != currentPeriodIndex()) {
DEBUG_MSG("Rotating airtimes to a new period = %u\n", currentPeriodIndex());
for (int i = periodsToLog - 2; i >= 0; --i) {
airtimes.periodTX[i + 1] = airtimes.periodTX[i];
airtimes.periodRX[i + 1] = airtimes.periodRX[i];
airtimes.periodRX_ALL[i + 1] = airtimes.periodRX_ALL[i];
}
airtimes.periodTX[0] = 0;
airtimes.periodRX[0] = 0;
airtimes.periodRX_ALL[0] = 0;
airtimes.lastPeriodIndex = currentPeriodIndex();
}
}
uint16_t *airtimeReport(reportTypes reportType)
uint32_t *airtimeReport(reportTypes reportType)
{
// currentHourIndexReset();
if (reportType == TX_LOG) {
return airtimes.periodTX;
@@ -86,3 +86,22 @@ uint32_t getSecondsSinceBoot()
{
return secSinceBoot;
}
AirTime::AirTime() : concurrency::OSThread("AirTime") {}
int32_t AirTime::runOnce()
{
//DEBUG_MSG("AirTime::runOnce()\n");
airtimeRotatePeriod();
secSinceBoot++;
/*
This actually doesn't need to be run once per second but we currently use it for the
secSinceBoot counter.
If we have a better counter of how long the device has been online (and not millis())
then we can change this to something less frequent. Maybe once ever 5 seconds?
*/
return (1000 * 1);
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include "concurrency/OSThread.h"
#include "configuration.h"
#include <Arduino.h>
#include <functional>
@@ -18,7 +19,7 @@
TX_LOG + RX_LOG = Total air time for a perticular meshtastic channel.
TX_LOG + RX_LOG = Total air time for a perticular meshtastic channel, including
other lora radios.
other lora radios.
RX_ALL_LOG - RX_LOG = Other lora radios on our frequency channel.
*/
@@ -26,13 +27,28 @@ enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG };
void logAirtime(reportTypes reportType, uint32_t airtime_ms);
void airtimeCalculator();
void airtimeRotatePeriod();
uint8_t currentPeriodIndex();
uint8_t getPeriodsToLog();
uint32_t getSecondsSinceBoot();
uint16_t *airtimeReport(reportTypes reportType);
uint32_t *airtimeReport(reportTypes reportType);
uint32_t getSecondsPerPeriod();
uint32_t getSecondsPerPeriod();
class AirTime : private concurrency::OSThread
{
public:
AirTime();
void logAirtime(reportTypes reportType, uint32_t airtime_ms);
protected:
virtual int32_t runOnce();
};
extern AirTime *airTime;

View File

@@ -28,6 +28,8 @@ void OSThread::setup()
OSThread::OSThread(const char *_name, uint32_t period, ThreadController *_controller)
: Thread(NULL, period), controller(_controller)
{
assertIsSetup();
ThreadName = _name;
if (controller)
@@ -81,4 +83,36 @@ void OSThread::run()
currentThread = NULL;
}
/**
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
*
* it is super important to never allocate those object statically. instead, you should explicitly
* new them at a point where you are guaranteed that other objects that this instance
* depends on have already been created.
*
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
* this makes it guaranteed that the global mainController is fully constructed first.
*/
bool hasBeenSetup;
void assertIsSetup()
{
/**
* Dear developer comrade - If this assert fails() that means you need to fix the following:
*
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
*
* it is super important to never allocate those object statically. instead, you should explicitly
* new them at a point where you are guaranteed that other objects that this instance
* depends on have already been created.
*
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
* this makes it guaranteed that the global mainController is fully constructed first.
*/
assert(hasBeenSetup);
}
} // namespace concurrency

View File

@@ -17,14 +17,14 @@ extern InterruptableDelay mainDelay;
/**
* @brief Base threading
*
*
* This is a pseudo threading layer that is super easy to port, well suited to our slow network and very ram & power efficient.
*
* TODO FIXME @geeksville
*
* move more things into OSThreads
* remove lock/lockguard
*
*
* move typedQueue into concurrency
* remove freertos from typedqueue
*/
@@ -42,7 +42,6 @@ class OSThread : public Thread
static bool showWaiting;
public:
/// For debug printing only (might be null)
static const OSThread *currentThread;
@@ -71,4 +70,19 @@ class OSThread : public Thread
virtual void run();
};
/**
* This flag is set **only** when setup() starts, to provide a way for us to check for sloppy static constructor calls.
* Call assertIsSetup() to force a crash if someone tries to create an instance too early.
*
* it is super important to never allocate those object statically. instead, you should explicitly
* new them at a point where you are guaranteed that other objects that this instance
* depends on have already been created.
*
* in particular, for OSThread that means "all instances must be declared via new() in setup() or later" -
* this makes it guaranteed that the global mainController is fully constructed first.
*/
extern bool hasBeenSetup;
void assertIsSetup();
} // namespace concurrency

View File

@@ -4,7 +4,6 @@
#include "EInkDisplay.h"
#include "SPILock.h"
#include "epd1in54.h" // Screen specific library
#include "graphics/configs.h"
#include <SPI.h>
#include <TFT_eSPI.h> // Graphics library and Sprite class

View File

@@ -31,11 +31,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graphics/images.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "meshwifi/meshwifi.h"
#include "plugins/TextMessagePlugin.h"
#include "target_specific.h"
#include "utils.h"
#ifndef NO_ESP32
#include "mesh/http/WiFiAPClient.h"
#endif
using namespace meshtastic; /** @todo remove */
namespace graphics
@@ -746,7 +749,7 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
textMessageObserver.observe(&textMessagePlugin);
textMessageObserver.observe(textMessagePlugin);
}
void Screen::forceDisplay()
@@ -895,10 +898,12 @@ void Screen::setFrames()
// call a method on debugInfoScreen object (for more details)
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
#ifndef NO_ESP32
if (isWifiAvailable()) {
// call a method on debugInfoScreen object (for more details)
normalFrames[numframes++] = &Screen::drawDebugInfoWiFiTrampoline;
}
#endif
ui.setFrames(normalFrames, numframes);
ui.enableAllIndicators();

View File

@@ -19,8 +19,7 @@
#include "concurrency/Periodic.h"
#include "graphics/Screen.h"
#include "main.h"
#include "meshwifi/meshhttp.h"
#include "meshwifi/meshwifi.h"
#include "plugins/Plugins.h"
#include "sleep.h"
#include "target_specific.h"
#include <OneButton.h>
@@ -28,6 +27,8 @@
// #include <driver/rtc_io.h>
#ifndef NO_ESP32
#include "mesh/http/WebServer.h"
#include "mesh/http/WiFiAPClient.h"
#include "nimble/BluetoothUtil.h"
#endif
@@ -275,6 +276,8 @@ RadioInterface *rIf = NULL;
void setup()
{
concurrency::hasBeenSetup = true;
#ifdef SEGGER_STDOUT_CH
SEGGER_RTT_ConfigUpBuffer(SEGGER_STDOUT_CH, NULL, NULL, 1024, SEGGER_RTT_MODE_NO_BLOCK_TRIM);
#endif
@@ -300,17 +303,20 @@ void setup()
digitalWrite(RESET_OLED, 1);
#endif
#ifdef BUTTON_PIN
#ifndef NO_ESP32
// If BUTTON_PIN is held down during the startup process,
// force the device to go into a SoftAP mode.
bool forceSoftAP = 0;
#ifdef BUTTON_PIN
#ifndef NO_ESP32
pinMode(BUTTON_PIN, INPUT);
#ifdef BUTTON_NEED_PULLUP
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
#endif
// BUTTON_PIN is pulled high by a 12k resistor.
if (!digitalRead(BUTTON_PIN)) {
forceSoftAP = 1;
DEBUG_MSG("-------------------- Setting forceSoftAP = 1\n");
DEBUG_MSG("Setting forceSoftAP = 1\n");
}
#endif
@@ -390,15 +396,15 @@ void setup()
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
#ifdef GENIEBLOCKS
//gps setup
pinMode (GPS_RESET_N, OUTPUT);
// gps setup
pinMode(GPS_RESET_N, OUTPUT);
pinMode(GPS_EXTINT, OUTPUT);
digitalWrite(GPS_RESET_N, HIGH);
digitalWrite(GPS_EXTINT, LOW);
//battery setup
// battery setup
// If we want to read battery level, we need to set BATTERY_EN_PIN pin to low.
// ToDo: For low power consumption after read battery level, set that pin to high.
pinMode (BATTERY_EN_PIN, OUTPUT);
pinMode(BATTERY_EN_PIN, OUTPUT);
digitalWrite(BATTERY_EN_PIN, LOW);
#endif
@@ -439,14 +445,17 @@ void setup()
service.init();
// Now that the mesh service is created, create any plugins
setupPlugins();
// Do this after service.init (because that clears error_code)
#ifdef AXP192_SLAVE_ADDRESS
if(!axp192_found)
if (!axp192_found)
recordCriticalError(CriticalErrorCode_NoAXP192); // Record a hardware fault for missing hardware
#endif
#endif
// Don't call screen setup until after nodedb is setup (because we need
// the current region name)
// Don't call screen setup until after nodedb is setup (because we need
// the current region name)
#if defined(ST7735_CS) || defined(HAS_EINK)
screen->setup();
#else
@@ -507,9 +516,17 @@ void setup()
}
#endif
#ifndef NO_ESP32
// Initialize Wifi
initWifi(forceSoftAP);
// Start web server thread.
webServerThread = new WebServerThread();
#endif
// Start airtime logger thread.
airTime = new AirTime();
if (!rIf)
recordCriticalError(CriticalErrorCode_NoRadio);
else
@@ -570,7 +587,7 @@ void loop()
#endif
// TODO: This should go into a thread handled by FreeRTOS.
handleWebResponse();
// handleWebResponse();
service.loop();
@@ -583,7 +600,4 @@ void loop()
// We want to sleep as long as possible here - because it saves power
mainDelay.delay(delayMsec);
// if (didWake) DEBUG_MSG("wake!\n");
// Handles cleanup for the airtime calculator.
airtimeCalculator();
}

View File

@@ -60,7 +60,8 @@ static int32_t sendOwnerCb()
currentGeneration = radioGeneration;
DEBUG_MSG("Sending our nodeinfo to mesh (wantReplies=%d)\n", requestReplies);
nodeInfoPlugin.sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
assert(nodeInfoPlugin);
nodeInfoPlugin->sendOurNodeInfo(NODENUM_BROADCAST, requestReplies); // Send our info (don't request replies)
return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000;
}
@@ -134,7 +135,8 @@ bool MeshService::reloadConfig()
/// The owner User record just got updated, update our node DB and broadcast the info into the mesh
void MeshService::reloadOwner()
{
nodeInfoPlugin.sendOurNodeInfo();
assert(nodeInfoPlugin);
nodeInfoPlugin->sendOurNodeInfo();
nodeDB.saveToDisk();
}
@@ -193,17 +195,40 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
assert(node);
DEBUG_MSG("Sending network ping to 0x%x, with position=%d, wantReplies=%d\n", dest, node->has_position, wantReplies);
assert(positionPlugin && nodeInfoPlugin);
if (node->has_position)
positionPlugin.sendOurPosition(dest, wantReplies);
positionPlugin->sendOurPosition(dest, wantReplies);
else
nodeInfoPlugin.sendOurNodeInfo(dest, wantReplies);
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
}
NodeInfo *MeshService::refreshMyNodeInfo() {
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
assert(node);
// We might not have a position yet for our local node, in that case, at least try to send the time
if(!node->has_position) {
memset(&node->position, 0, sizeof(node->position));
node->has_position = true;
}
Position &position = node->position;
// Update our local node info with our position (even if we don't decide to update anyone else)
position.time = getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid.
position.battery_level = powerStatus->getBatteryChargePercent();
updateBatteryLevel(position.battery_level);
return node;
}
int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
{
// Update our local node info with our position (even if we don't decide to update anyone else)
Position pos = Position_init_default;
NodeInfo *node = refreshMyNodeInfo();
Position pos = node->position;
if (gps->hasLock()) {
if (gps->altitude != 0)
@@ -214,21 +239,16 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
else {
// The GPS has lost lock, if we are fixed position we should just keep using
// the old position
if(radioConfig.preferences.fixed_position) {
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
assert(node);
assert(node->has_position);
pos = node->position;
if(!radioConfig.preferences.fixed_position) {
DEBUG_MSG("WARNING: Using fixed position\n");
} else {
// throw away old position
pos.latitude_i = 0;
pos.longitude_i = 0;
pos.altitude = 0;
}
}
pos.time = getValidTime(RTCQualityGPS);
// Include our current battery voltage in our position announcement
pos.battery_level = powerStatus->getBatteryChargePercent();
updateBatteryLevel(pos.battery_level);
DEBUG_MSG("got gps notify time=%u, lat=%d, bat=%d\n", pos.latitude_i, pos.time, pos.battery_level);
// Update our current position in the local DB
@@ -247,7 +267,8 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
currentGeneration = radioGeneration;
DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies);
positionPlugin.sendOurPosition(NODENUM_BROADCAST, requestReplies);
assert(positionPlugin);
positionPlugin->sendOurPosition(NODENUM_BROADCAST, requestReplies);
}
return 0;

View File

@@ -79,6 +79,9 @@ class MeshService
/// cache
void sendToMesh(MeshPacket *p);
/// Pull the latest power and time info into my nodeinfo
NodeInfo *refreshMyNodeInfo();
private:
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh

View File

@@ -5,6 +5,7 @@
#include "FS.h"
#include "CryptoEngine.h"
#include "FSCommon.h"
#include "GPS.h"
#include "MeshRadio.h"
#include "NodeDB.h"
@@ -15,11 +16,13 @@
#include "configuration.h"
#include "error.h"
#include "mesh-pb-constants.h"
#include "meshwifi/meshwifi.h"
#include "FSCommon.h"
#include <pb_decode.h>
#include <pb_encode.h>
#ifndef NO_ESP32
#include "mesh/http/WiFiAPClient.h"
#endif
NodeDB nodeDB;
// we have plenty of ram so statically alloc this tempbuf (for now)
@@ -28,7 +31,7 @@ MyNodeInfo &myNodeInfo = devicestate.my_node;
RadioConfig &radioConfig = devicestate.radio;
ChannelSettings &channelSettings = radioConfig.channel_settings;
/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings
/** The current change # for radio settings. Starts at 0 on boot and any time the radio settings
* might have changed is incremented. Allows others to detect they might now be on a new channel.
*/
uint32_t radioGeneration;
@@ -41,8 +44,6 @@ DeviceState versions used to be defined in the .proto file but really only this
#define DEVICESTATE_CUR_VER 11
#define DEVICESTATE_MIN_VER DEVICESTATE_CUR_VER
// FIXME - move this somewhere else
extern void getMacAddr(uint8_t *dmac);
@@ -90,7 +91,7 @@ const char *getChannelName()
static char buf[32];
char suffix;
if(channelSettings.psk.size != 1) {
if (channelSettings.psk.size != 1) {
// We have a standard PSK, so generate a letter based hash.
uint8_t code = 0;
for (int i = 0; i < activePSKSize; i++)
@@ -140,32 +141,40 @@ bool NodeDB::resetRadioConfig()
}
// Convert the old string "Default" to our new short representation
if(strcmp(channelSettings.name, "Default") == 0)
if (strcmp(channelSettings.name, "Default") == 0)
*channelSettings.name = '\0';
// Convert the short "" representation for Default into a usable string
channelName = channelSettings.name;
if(!*channelName) { // emptystring
if (!*channelName) { // emptystring
// Per mesh.proto spec, if bandwidth is specified we must ignore modemConfig enum, we assume that in that case
// the app fucked up and forgot to set channelSettings.name
channelName = "Unset";
if(channelSettings.bandwidth == 0) switch(channelSettings.modem_config) {
if (channelSettings.bandwidth != 0)
channelName = "Unset";
else
switch (channelSettings.modem_config) {
case ChannelSettings_ModemConfig_Bw125Cr45Sf128:
channelName = "Medium"; break;
channelName = "Medium";
break;
case ChannelSettings_ModemConfig_Bw500Cr45Sf128:
channelName = "ShortFast"; break;
channelName = "ShortFast";
break;
case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512:
channelName = "LongAlt"; break;
channelName = "LongAlt";
break;
case ChannelSettings_ModemConfig_Bw125Cr48Sf4096:
channelName = "LongSlow"; break;
channelName = "LongSlow";
break;
default:
channelName = "Invalid"; break;
}
channelName = "Invalid";
break;
}
}
// Convert any old usage of the defaultpsk into our new short representation.
if(channelSettings.psk.size == sizeof(defaultpsk) &&
memcmp(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)) == 0) {
if (channelSettings.psk.size == sizeof(defaultpsk) &&
memcmp(channelSettings.psk.bytes, defaultpsk, sizeof(defaultpsk)) == 0) {
*channelSettings.psk.bytes = 1;
channelSettings.psk.size = 1;
}
@@ -173,10 +182,10 @@ bool NodeDB::resetRadioConfig()
// Convert the short single byte variants of psk into variant that can be used more generally
memcpy(activePSK, channelSettings.psk.bytes, channelSettings.psk.size);
activePSKSize = channelSettings.psk.size;
if(activePSKSize == 1) {
if (activePSKSize == 1) {
uint8_t pskIndex = activePSK[0];
DEBUG_MSG("Expanding short PSK #%d\n", pskIndex);
if(pskIndex == 0)
if (pskIndex == 0)
activePSKSize = 0; // Turn off encryption
else {
memcpy(activePSK, defaultpsk, sizeof(defaultpsk));
@@ -271,12 +280,13 @@ void NodeDB::init()
myNodeInfo.node_num_bits = sizeof(NodeNum) * 8;
myNodeInfo.packet_id_bits = sizeof(PacketId) * 8;
myNodeInfo.error_code = CriticalErrorCode_None; // For the error code, only show values from this boot (discard value from flash)
myNodeInfo.error_code =
CriticalErrorCode_None; // For the error code, only show values from this boot (discard value from flash)
myNodeInfo.error_address = 0;
// likewise - we always want the app requirements to come from the running appload
myNodeInfo.min_app_version = 20120; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
pickNewNodeNum();

View File

@@ -134,6 +134,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
fromRadioScratch.which_variant = FromRadio_my_info_tag;
fromRadioScratch.variant.my_info = myNodeInfo;
state = STATE_SEND_RADIO;
service.refreshMyNodeInfo(); // Update my NodeInfo because the client will be asking for it soon.
break;
case STATE_SEND_RADIO:

View File

@@ -97,7 +97,8 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
// Count the packet toward our TX airtime utilization.
// We only count it if it can be added to the TX queue.
logAirtime(TX_LOG, xmitMsec);
airTime->logAirtime(TX_LOG, xmitMsec);
//airTime.logAirtime(TX_LOG, xmitMsec);
// We want all sending/receiving to be done by our daemon thread, We use a delay here because this packet might have been sent
// in response to a packet we just received. So we want to make sure the other side has had a chance to reconfigure its radio
@@ -216,7 +217,8 @@ void RadioLibInterface::handleReceiveInterrupt()
size_t length = iface->getPacketLength();
xmitMsec = getPacketTime(length);
logAirtime(RX_ALL_LOG, xmitMsec);
airTime->logAirtime(RX_ALL_LOG, xmitMsec);
//airTime.logAirtime(RX_ALL_LOG, xmitMsec);
int state = iface->readData(radiobuf, length);
if (state != ERR_NONE) {
@@ -258,7 +260,8 @@ void RadioLibInterface::handleReceiveInterrupt()
printPacket("Lora RX", mp);
xmitMsec = getPacketTime(mp);
logAirtime(RX_LOG, xmitMsec);
airTime->logAirtime(RX_LOG, xmitMsec);
//airTime.logAirtime(RX_LOG, xmitMsec);
deliverToReceiver(mp);
}

View File

@@ -80,7 +80,7 @@ extern const pb_msgdesc_t DeviceState_msg;
#define DeviceState_fields &DeviceState_msg
/* Maximum encoded size of messages (where known) */
#define DeviceState_size 6176
#define DeviceState_size 6206
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -57,3 +57,4 @@ PB_BIND(ToRadio, ToRadio, 2)

View File

@@ -35,6 +35,26 @@ typedef enum _RegionCode {
RegionCode_TW = 8
} RegionCode;
typedef enum _ChargeCurrent {
ChargeCurrent_MAUnset = 0,
ChargeCurrent_MA100 = 1,
ChargeCurrent_MA190 = 2,
ChargeCurrent_MA280 = 3,
ChargeCurrent_MA360 = 4,
ChargeCurrent_MA450 = 5,
ChargeCurrent_MA550 = 6,
ChargeCurrent_MA630 = 7,
ChargeCurrent_MA700 = 8,
ChargeCurrent_MA780 = 9,
ChargeCurrent_MA880 = 10,
ChargeCurrent_MA960 = 11,
ChargeCurrent_MA1000 = 12,
ChargeCurrent_MA1080 = 13,
ChargeCurrent_MA1160 = 14,
ChargeCurrent_MA1240 = 15,
ChargeCurrent_MA1320 = 16
} ChargeCurrent;
typedef enum _GpsOperation {
GpsOperation_GpsOpUnset = 0,
GpsOperation_GpsOpMobile = 2,
@@ -145,6 +165,7 @@ typedef struct _RadioConfig_UserPreferences {
char wifi_password[64];
bool wifi_ap_mode;
RegionCode region;
ChargeCurrent charge_current;
LocationSharing location_share;
GpsOperation gps_operation;
uint32_t gps_update_interval;
@@ -156,6 +177,11 @@ typedef struct _RadioConfig_UserPreferences {
bool debug_log_enabled;
pb_size_t ignore_incoming_count;
uint32_t ignore_incoming[3];
bool serialplugin_enabled;
bool serialplugin_echo;
uint32_t serialplugin_rxd;
uint32_t serialplugin_txd;
uint32_t serialplugin_timeout;
} RadioConfig_UserPreferences;
typedef struct _RouteDiscovery {
@@ -265,6 +291,10 @@ typedef struct _ToRadio {
#define _RegionCode_MAX RegionCode_TW
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TW+1))
#define _ChargeCurrent_MIN ChargeCurrent_MAUnset
#define _ChargeCurrent_MAX ChargeCurrent_MA1320
#define _ChargeCurrent_ARRAYSIZE ((ChargeCurrent)(ChargeCurrent_MA1320+1))
#define _GpsOperation_MIN GpsOperation_GpsOpUnset
#define _GpsOperation_MAX GpsOperation_GpsOpDisabled
#define _GpsOperation_ARRAYSIZE ((GpsOperation)(GpsOperation_GpsOpDisabled+1))
@@ -299,7 +329,7 @@ extern "C" {
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0, 0}
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0}
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default, false, ChannelSettings_init_default}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0}
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
@@ -313,7 +343,7 @@ extern "C" {
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0, 0}
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0, 0, 0, 0}
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero, false, ChannelSettings_init_zero}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0}
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
@@ -371,6 +401,7 @@ extern "C" {
#define RadioConfig_UserPreferences_wifi_password_tag 13
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
#define RadioConfig_UserPreferences_region_tag 15
#define RadioConfig_UserPreferences_charge_current_tag 16
#define RadioConfig_UserPreferences_location_share_tag 32
#define RadioConfig_UserPreferences_gps_operation_tag 33
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
@@ -381,6 +412,11 @@ extern "C" {
#define RadioConfig_UserPreferences_factory_reset_tag 100
#define RadioConfig_UserPreferences_debug_log_enabled_tag 101
#define RadioConfig_UserPreferences_ignore_incoming_tag 103
#define RadioConfig_UserPreferences_serialplugin_enabled_tag 120
#define RadioConfig_UserPreferences_serialplugin_echo_tag 121
#define RadioConfig_UserPreferences_serialplugin_rxd_tag 122
#define RadioConfig_UserPreferences_serialplugin_txd_tag 123
#define RadioConfig_UserPreferences_serialplugin_timeout_tag 124
#define RouteDiscovery_route_tag 2
#define User_id_tag 1
#define User_long_name_tag 2
@@ -533,6 +569,7 @@ X(a, STATIC, SINGULAR, STRING, wifi_ssid, 12) \
X(a, STATIC, SINGULAR, STRING, wifi_password, 13) \
X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \
X(a, STATIC, SINGULAR, UENUM, region, 15) \
X(a, STATIC, SINGULAR, UENUM, charge_current, 16) \
X(a, STATIC, SINGULAR, UENUM, location_share, 32) \
X(a, STATIC, SINGULAR, UENUM, gps_operation, 33) \
X(a, STATIC, SINGULAR, UINT32, gps_update_interval, 34) \
@@ -542,7 +579,12 @@ X(a, STATIC, SINGULAR, BOOL, is_low_power, 38) \
X(a, STATIC, SINGULAR, BOOL, fixed_position, 39) \
X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \
X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103)
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
X(a, STATIC, SINGULAR, BOOL, serialplugin_enabled, 120) \
X(a, STATIC, SINGULAR, BOOL, serialplugin_echo, 121) \
X(a, STATIC, SINGULAR, UINT32, serialplugin_rxd, 122) \
X(a, STATIC, SINGULAR, UINT32, serialplugin_txd, 123) \
X(a, STATIC, SINGULAR, UINT32, serialplugin_timeout, 124)
#define RadioConfig_UserPreferences_CALLBACK NULL
#define RadioConfig_UserPreferences_DEFAULT NULL
@@ -654,13 +696,13 @@ extern const pb_msgdesc_t ToRadio_msg;
#define SubPacket_size 275
#define MeshPacket_size 320
#define ChannelSettings_size 95
#define RadioConfig_size 319
#define RadioConfig_UserPreferences_size 219
#define RadioConfig_size 349
#define RadioConfig_UserPreferences_size 249
#define NodeInfo_size 132
#define MyNodeInfo_size 106
#define LogRecord_size 81
#define FromRadio_size 329
#define ToRadio_size 323
#define FromRadio_size 358
#define ToRadio_size 353
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -18,13 +18,15 @@ typedef enum _PortNum {
PortNum_NODEINFO_APP = 4,
PortNum_REPLY_APP = 32,
PortNum_IP_TUNNEL_APP = 33,
PortNum_PRIVATE_APP = 256
PortNum_SERIAL_APP = 64,
PortNum_PRIVATE_APP = 256,
PortNum_ATAK_FORWARDER = 257
} PortNum;
/* Helper constants for enums */
#define _PortNum_MIN PortNum_UNKNOWN_APP
#define _PortNum_MAX PortNum_PRIVATE_APP
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_PRIVATE_APP+1))
#define _PortNum_MAX PortNum_ATAK_FORWARDER
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_ATAK_FORWARDER+1))
#ifdef __cplusplus

View File

@@ -1,23 +1,20 @@
#include "meshwifi/meshhttp.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "airtime.h"
#include "configuration.h"
#include "esp_task_wdt.h"
#include "main.h"
#include "meshhttpStatic.h"
#include "meshwifi/meshwifi.h"
#include "mesh/http/ContentHelper.h"
#include "mesh/http/ContentStatic.h"
#include "mesh/http/WiFiAPClient.h"
#include "power.h"
#include "sleep.h"
#include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp>
#include <HTTPURLEncodedBodyParser.hpp>
#include <SPIFFS.h>
#include <WebServer.h>
#include <WiFi.h>
// Persistant Data Storage
#include <Preferences.h>
Preferences prefs;
#ifndef NO_ESP32
#include "esp_task_wdt.h"
#endif
/*
Including the esp32_https_server library will trigger a compile time error. I've
@@ -42,40 +39,7 @@ Preferences prefs;
// The HTTPS Server comes in a separate namespace. For easier use, include it here.
using namespace httpsserver;
SSLCert *cert;
HTTPSServer *secureServer;
HTTPServer *insecureServer;
// Our API to handle messages to and from the radio.
HttpAPI webAPI;
// Declare some handler functions for the various URLs on the server
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res);
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res);
void handleStyleCSS(HTTPRequest *req, HTTPResponse *res);
void handleHotspot(HTTPRequest *req, HTTPResponse *res);
void handleFavicon(HTTPRequest *req, HTTPResponse *res);
void handleRoot(HTTPRequest *req, HTTPResponse *res);
void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res);
void handleStaticPost(HTTPRequest *req, HTTPResponse *res);
void handleStatic(HTTPRequest *req, HTTPResponse *res);
void handleRestart(HTTPRequest *req, HTTPResponse *res);
void handle404(HTTPRequest *req, HTTPResponse *res);
void handleFormUpload(HTTPRequest *req, HTTPResponse *res);
void handleScanNetworks(HTTPRequest *req, HTTPResponse *res);
void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res);
void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res);
void handleReport(HTTPRequest *req, HTTPResponse *res);
void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
void middlewareSession(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
bool isWebServerReady = 0;
bool isCertReady = 0;
uint32_t timeSpeedUp = 0;
#include "mesh/http/ContentHandler.h"
// We need to specify some content-type mapping, so the resources get delivered with the
// right content type and are displayed correctly in the browser
@@ -86,161 +50,32 @@ char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"}
{".css", "text/css"}, {".ico", "image/vnd.microsoft.icon"},
{".svg", "image/svg+xml"}, {"", ""}};
void handleWebResponse()
// Our API to handle messages to and from the radio.
HttpAPI webAPI;
uint32_t numberOfRequests = 0;
uint32_t timeSpeedUp = 0;
uint32_t getTimeSpeedUp()
{
if (isWifiAvailable() == 0) {
return;
}
if (isWebServerReady) {
// We're going to handle the DNS responder here so it
// will be ignored by the NRF boards.
handleDNSResponse();
secureServer->loop();
insecureServer->loop();
}
/*
Slow down the CPU if we have not received a request within the last few
seconds.
*/
if (millis() - timeSpeedUp >= (25 * 1000)) {
setCpuFrequencyMhz(80);
timeSpeedUp = millis();
}
return timeSpeedUp;
}
void taskCreateCert(void *parameter)
void setTimeSpeedUp()
{
prefs.begin("MeshtasticHTTPS", false);
// Delete the saved certs
if (0) {
DEBUG_MSG("Deleting any saved SSL keys ...\n");
// prefs.clear();
prefs.remove("PK");
prefs.remove("cert");
}
size_t pkLen = prefs.getBytesLength("PK");
size_t certLen = prefs.getBytesLength("cert");
DEBUG_MSG("Checking if we have a previously saved SSL Certificate.\n");
if (pkLen && certLen) {
DEBUG_MSG("Existing SSL Certificate found!\n");
} else {
DEBUG_MSG("Creating the certificate. This may take a while. Please wait...\n");
yield();
cert = new SSLCert();
yield();
int createCertResult = createSelfSignedCert(*cert, KEYSIZE_2048, "CN=meshtastic.local,O=Meshtastic,C=US",
"20190101000000", "20300101000000");
yield();
if (createCertResult != 0) {
DEBUG_MSG("Creating the certificate failed\n");
// Serial.printf("Creating the certificate failed. Error Code = 0x%02X, check SSLCert.hpp for details",
// createCertResult);
// while (true)
// delay(500);
} else {
DEBUG_MSG("Creating the certificate was successful\n");
DEBUG_MSG("Created Private Key: %d Bytes\n", cert->getPKLength());
// for (int i = 0; i < cert->getPKLength(); i++)
// Serial.print(cert->getPKData()[i], HEX);
// Serial.println();
DEBUG_MSG("Created Certificate: %d Bytes\n", cert->getCertLength());
// for (int i = 0; i < cert->getCertLength(); i++)
// Serial.print(cert->getCertData()[i], HEX);
// Serial.println();
prefs.putBytes("PK", (uint8_t *)cert->getPKData(), cert->getPKLength());
prefs.putBytes("cert", (uint8_t *)cert->getCertData(), cert->getCertLength());
}
}
isCertReady = 1;
vTaskDelete(NULL);
timeSpeedUp = millis();
}
void createSSLCert()
void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
{
if (isWifiAvailable() == 0) {
return;
}
// Create a new process just to handle creating the cert.
// This is a workaround for Bug: https://github.com/fhessel/esp32_https_server/issues/48
// jm@casler.org (Oct 2020)
xTaskCreate(taskCreateCert, /* Task function. */
"createCert", /* String with name of task. */
16384, /* Stack size in bytes. */
NULL, /* Parameter passed as input of the task */
16, /* Priority of the task. */
NULL); /* Task handle. */
DEBUG_MSG("Waiting for SSL Cert to be generated.\n");
while (!isCertReady) {
DEBUG_MSG(".");
delay(1000);
yield();
esp_task_wdt_reset();
}
DEBUG_MSG("SSL Cert Ready!\n");
}
void initWebServer()
{
DEBUG_MSG("Initializing Web Server ...\n");
prefs.begin("MeshtasticHTTPS", false);
size_t pkLen = prefs.getBytesLength("PK");
size_t certLen = prefs.getBytesLength("cert");
DEBUG_MSG("Checking if we have a previously saved SSL Certificate.\n");
if (pkLen && certLen) {
uint8_t *pkBuffer = new uint8_t[pkLen];
prefs.getBytes("PK", pkBuffer, pkLen);
uint8_t *certBuffer = new uint8_t[certLen];
prefs.getBytes("cert", certBuffer, certLen);
cert = new SSLCert(certBuffer, certLen, pkBuffer, pkLen);
DEBUG_MSG("Retrieved Private Key: %d Bytes\n", cert->getPKLength());
// DEBUG_MSG("Retrieved Private Key: " + String(cert->getPKLength()) + " Bytes");
// for (int i = 0; i < cert->getPKLength(); i++)
// Serial.print(cert->getPKData()[i], HEX);
// Serial.println();
DEBUG_MSG("Retrieved Certificate: %d Bytes\n", cert->getCertLength());
// for (int i = 0; i < cert->getCertLength(); i++)
// Serial.print(cert->getCertData()[i], HEX);
// Serial.println();
} else {
DEBUG_MSG("Web Server started without SSL keys! How did this happen?\n");
}
// We can now use the new certificate to setup our server as usual.
secureServer = new HTTPSServer(cert);
insecureServer = new HTTPServer();
// For every resource available on the server, we need to create a ResourceNode
// The ResourceNode links URL and HTTP method to a handler function
ResourceNode *nodeAPIv1ToRadioOptions = new ResourceNode("/api/v1/toradio", "OPTIONS", &handleAPIv1ToRadio);
ResourceNode *nodeAPIv1ToRadio = new ResourceNode("/api/v1/toradio", "PUT", &handleAPIv1ToRadio);
ResourceNode *nodeAPIv1FromRadio = new ResourceNode("/api/v1/fromradio", "GET", &handleAPIv1FromRadio);
ResourceNode *nodeHotspot = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
ResourceNode *nodeFavicon = new ResourceNode("/favicon.ico", "GET", &handleFavicon);
ResourceNode *nodeRoot = new ResourceNode("/", "GET", &handleRoot);
@@ -253,7 +88,7 @@ void initWebServer()
ResourceNode *nodeJsonScanNetworks = new ResourceNode("/json/scanNetworks", "GET", &handleScanNetworks);
ResourceNode *nodeJsonBlinkLED = new ResourceNode("/json/blink", "POST", &handleBlinkLED);
ResourceNode *nodeJsonReport = new ResourceNode("/json/report", "GET", &handleReport);
ResourceNode *nodeJsonSpiffsBrowseStatic = new ResourceNode("/json/spiffs/browse/static/", "GET", &handleSpiffsBrowseStatic);
ResourceNode *nodeJsonSpiffsBrowseStatic = new ResourceNode("/json/spiffs/browse/static", "GET", &handleSpiffsBrowseStatic);
ResourceNode *nodeJsonDelete = new ResourceNode("/json/spiffs/delete/static", "DELETE", &handleSpiffsDeleteStatic);
// Secure nodes
@@ -297,16 +132,6 @@ void initWebServer()
insecureServer->setDefaultNode(node404);
insecureServer->addMiddleware(&middlewareSpeedUp160);
DEBUG_MSG("Starting Web Servers...\n");
secureServer->start();
insecureServer->start();
if (secureServer->isRunning() && insecureServer->isRunning()) {
DEBUG_MSG("HTTP and HTTPS Web Servers Ready! :-) \n");
isWebServerReady = 1;
} else {
DEBUG_MSG("HTTP and HTTPS Web Servers Failed! ;-( \n");
}
}
void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<void()> next)
@@ -319,7 +144,9 @@ void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<voi
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
setCpuFrequencyMhz(240);
timeSpeedUp = millis();
setTimeSpeedUp();
numberOfRequests++;
}
void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<void()> next)
@@ -337,7 +164,108 @@ void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<voi
if (getCpuFrequencyMhz() != 240) {
setCpuFrequencyMhz(160);
}
timeSpeedUp = millis();
setTimeSpeedUp();
numberOfRequests++;
}
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
{
DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1FromRadio\n");
/*
For documentation, see:
https://github.com/meshtastic/Meshtastic-device/wiki/HTTP-REST-API-discussion
https://github.com/meshtastic/Meshtastic-device/blob/master/docs/software/device-api.md
Example:
http://10.10.30.198/api/v1/fromradio
*/
// Get access to the parameters
ResourceParameters *params = req->getParams();
// std::string paramAll = "all";
std::string valueAll;
// Status code is 200 OK by default.
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/Meshtastic-protobufs/master/mesh.proto");
uint8_t txBuf[MAX_STREAM_BUF_SIZE];
uint32_t len = 1;
if (params->getQueryParameter("all", valueAll)) {
// If all is ture, return all the buffers we have available
// to us at this point in time.
if (valueAll == "true") {
while (len) {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
// Otherwise, just return one protobuf
} else {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
// the param "all" was not spcified. Return just one protobuf
} else {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
DEBUG_MSG("--------------- webAPI handleAPIv1FromRadio, len %d\n", len);
}
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
{
DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1ToRadio\n");
/*
For documentation, see:
https://github.com/meshtastic/Meshtastic-device/wiki/HTTP-REST-API-discussion
https://github.com/meshtastic/Meshtastic-device/blob/master/docs/software/device-api.md
Example:
http://10.10.30.198/api/v1/toradio
*/
// Status code is 200 OK by default.
res->setHeader("Content-Type", "application/x-protobuf");
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/Meshtastic-protobufs/master/mesh.proto");
if (req->getMethod() == "OPTIONS") {
res->setStatusCode(204); // Success with no content
res->print("");
return;
}
byte buffer[MAX_TO_FROM_RADIO_SIZE];
size_t s = req->readBytes(buffer, MAX_TO_FROM_RADIO_SIZE);
DEBUG_MSG("Received %d bytes from PUT request\n", s);
webAPI.handleToRadio(buffer, s);
res->write(buffer, s);
DEBUG_MSG("--------------- webAPI handleAPIv1ToRadio\n");
}
void handleFavicon(HTTPRequest *req, HTTPResponse *res)
{
// Set Content-Type
res->setHeader("Content-Type", "image/vnd.microsoft.icon");
// Write data from header file
res->write(FAVICON_DATA, FAVICON_LENGTH);
}
void handleStaticPost(HTTPRequest *req, HTTPResponse *res)
@@ -414,7 +342,8 @@ void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
// jm
res->setHeader("Content-Type", "application/json");
// res->setHeader("Content-Type", "text/html");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
File root = SPIFFS.open("/");
@@ -469,6 +398,8 @@ void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
std::string paramValDelete;
res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "DELETE");
if (params->getQueryParameter("delete", paramValDelete)) {
std::string pathDelete = "/" + paramValDelete;
if (SPIFFS.remove(pathDelete.c_str())) {
@@ -487,6 +418,67 @@ void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
}
}
/*
To convert text to c strings:
https://tomeko.net/online_tools/cpp_text_escape.php?lang=en
*/
void handleRoot(HTTPRequest *req, HTTPResponse *res)
{
res->setHeader("Content-Type", "text/html");
res->setHeader("Set-Cookie",
"mt_session=" + httpsserver::intToString(random(1, 9999999)) + "; Expires=Wed, 20 Apr 2049 4:20:00 PST");
std::string cookie = req->getHeader("Cookie");
// String cookieString = cookie.c_str();
// uint8_t nameIndex = cookieString.indexOf("mt_session");
// DEBUG_MSG(cookie.c_str());
std::string filename = "/static/index.html";
std::string filenameGzip = "/static/index.html.gz";
if (!SPIFFS.exists(filename.c_str()) && !SPIFFS.exists(filenameGzip.c_str())) {
// Send "404 Not Found" as response, as the file doesn't seem to exist
res->setStatusCode(404);
res->setStatusText("Not found");
res->println("404 Not Found");
res->printf("<p>File not found: %s</p>\n", filename.c_str());
res->printf("<p></p>\n");
res->printf("<p>You have gotten this error because the filesystem for the web server has not been loaded.</p>\n");
res->printf("<p>Please review the 'Common Problems' section of the <a "
"href=https://github.com/meshtastic/Meshtastic-device/wiki/"
"How-to-use-the-Meshtastic-Web-Interface-over-WiFi>web interface</a> documentation.</p>\n");
return;
}
// Try to open the file from SPIFFS
File file;
if (SPIFFS.exists(filename.c_str())) {
file = SPIFFS.open(filename.c_str());
if (!file.available()) {
DEBUG_MSG("File not available - %s\n", filename.c_str());
}
} else if (SPIFFS.exists(filenameGzip.c_str())) {
file = SPIFFS.open(filenameGzip.c_str());
res->setHeader("Content-Encoding", "gzip");
if (!file.available()) {
DEBUG_MSG("File not available\n");
}
}
// Read the file from SPIFFS and write it to the HTTP response body
size_t length = 0;
do {
char buffer[256];
length = file.read((uint8_t *)buffer, 256);
std::string bufferString(buffer, length);
res->write((uint8_t *)bufferString.c_str(), bufferString.size());
} while (length > 0);
}
void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res)
{
// Get access to the parameters
@@ -494,6 +486,9 @@ void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res)
std::string paramValDelete;
std::string paramValEdit;
DEBUG_MSG("Static Browse - Disabling keep-alive\n");
res->setHeader("Connection", "close");
// Set a default content type
res->setHeader("Content-Type", "text/html");
@@ -700,6 +695,11 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
{
DEBUG_MSG("Form Upload - Disabling keep-alive\n");
res->setHeader("Connection", "close");
DEBUG_MSG("Form Upload - Set frequency to 240mhz\n");
// The upload process is very CPU intensive. Let's speed things up a bit.
setCpuFrequencyMhz(240);
@@ -708,6 +708,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
// Then we select the body parser based on the encoding.
// Actually we do this only for documentary purposes, we know the form is going
// to be multipart/form-data.
DEBUG_MSG("Form Upload - Creating body parser reference\n");
HTTPBodyParser *parser;
std::string contentType = req->getHeader("Content-Type");
@@ -723,6 +724,7 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
// Now, we can decide based on the content type:
if (contentType == "multipart/form-data") {
DEBUG_MSG("Form Upload - multipart/form-data\n");
parser = new HTTPMultipartBodyParser(req);
} else {
Serial.printf("Unknown POST Content-Type: %s\n", contentType.c_str());
@@ -757,21 +759,21 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
// Double check that it is what we expect
if (name != "file") {
DEBUG_MSG("Skipping unexpected field");
DEBUG_MSG("Skipping unexpected field\n");
res->println("<p>No file found.</p>");
return;
}
// Double check that it is what we expect
if (filename == "") {
DEBUG_MSG("Skipping unexpected field");
DEBUG_MSG("Skipping unexpected field\n");
res->println("<p>No file found.</p>");
return;
}
// SPIFFS limits the total lenth of a path + file to 31 characters.
if (filename.length() + 8 > 31) {
DEBUG_MSG("Uploaded filename too long!");
DEBUG_MSG("Uploaded filename too long!\n");
res->println("<p>Uploaded filename too long! Limit of 23 characters.</p>");
delete parser;
return;
@@ -806,10 +808,10 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
return;
}
//if (readLength) {
file.write(buf, readLength);
fileLength += readLength;
DEBUG_MSG("File Length %i\n", fileLength);
// if (readLength) {
file.write(buf, readLength);
fileLength += readLength;
DEBUG_MSG("File Length %i\n", fileLength);
//}
}
// enableLoopWDT();
@@ -824,6 +826,118 @@ void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
delete parser;
}
void handleReport(HTTPRequest *req, HTTPResponse *res)
{
ResourceParameters *params = req->getParams();
std::string content;
if (!params->getQueryParameter("content", content)) {
content = "json";
}
if (content == "json") {
res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
} else {
res->setHeader("Content-Type", "text/html");
res->println("<pre>");
}
res->println("{");
res->println("\"data\": {");
res->println("\"airtime\": {");
uint32_t *logArray;
res->print("\"tx_log\": [");
logArray = airtimeReport(TX_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint32_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->print("\"rx_log\": [");
logArray = airtimeReport(RX_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint32_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->print("\"rx_all_log\": [");
logArray = airtimeReport(RX_ALL_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint32_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->printf("\"seconds_since_boot\": %u,\n", getSecondsSinceBoot());
res->printf("\"seconds_per_period\": %u,\n", getSecondsPerPeriod());
res->printf("\"periods_to_log\": %u\n", getPeriodsToLog());
res->println("},");
res->println("\"wifi\": {");
res->printf("\"web_request_count\": %d,\n", numberOfRequests);
res->println("\"rssi\": " + String(WiFi.RSSI()) + ",");
if (radioConfig.preferences.wifi_ap_mode || isSoftAPForced()) {
res->println("\"ip\": \"" + String(WiFi.softAPIP().toString().c_str()) + "\"");
} else {
res->println("\"ip\": \"" + String(WiFi.localIP().toString().c_str()) + "\"");
}
res->println("},");
res->println("\"memory\": {");
res->printf("\"heap_total\": %d,\n", ESP.getHeapSize());
res->printf("\"heap_free\": %d,\n", ESP.getFreeHeap());
res->printf("\"psram_total\": %d,\n", ESP.getPsramSize());
res->printf("\"psram_free\": %d,\n", ESP.getFreePsram());
res->println("\"spiffs_total\" : " + String(SPIFFS.totalBytes()) + ",");
res->println("\"spiffs_used\" : " + String(SPIFFS.usedBytes()) + ",");
res->println("\"spiffs_free\" : " + String(SPIFFS.totalBytes() - SPIFFS.usedBytes()));
res->println("},");
res->println("\"power\": {");
res->printf("\"battery_percent\": %u,\n", powerStatus->getBatteryChargePercent());
res->printf("\"battery_voltage_mv\": %u,\n", powerStatus->getBatteryVoltageMv());
res->printf("\"has_battery\": %s,\n", BoolToString(powerStatus->getHasBattery()));
res->printf("\"has_usb\": %s,\n", BoolToString(powerStatus->getHasUSB()));
res->printf("\"is_charging\": %s\n", BoolToString(powerStatus->getIsCharging()));
res->println("}");
res->println("},");
res->println("\"status\": \"ok\"");
res->println("}");
}
// --------
void handle404(HTTPRequest *req, HTTPResponse *res)
{
@@ -861,165 +975,18 @@ void handleHotspot(HTTPRequest *req, HTTPResponse *res)
// Status code is 200 OK by default.
// We want to deliver a simple HTML page, so we send a corresponding content type:
res->setHeader("Content-Type", "text/html");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
// res->println("<!DOCTYPE html>");
res->println("<meta http-equiv=\"refresh\" content=\"0;url=/\" />\n");
}
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
{
DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1FromRadio\n");
/*
For documentation, see:
https://github.com/meshtastic/Meshtastic-device/wiki/HTTP-REST-API-discussion
https://github.com/meshtastic/Meshtastic-device/blob/master/docs/software/device-api.md
Example:
http://10.10.30.198/api/v1/fromradio
*/
// Get access to the parameters
ResourceParameters *params = req->getParams();
// std::string paramAll = "all";
std::string valueAll;
// Status code is 200 OK by default.
res->setHeader("Content-Type", "application/x-protobuf");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "PUT, GET");
res->setHeader("X-Protobuf-Schema", "https://raw.githubusercontent.com/meshtastic/Meshtastic-protobufs/master/mesh.proto");
uint8_t txBuf[MAX_STREAM_BUF_SIZE];
uint32_t len = 1;
if (params->getQueryParameter("all", valueAll)) {
// If all is ture, return all the buffers we have available
// to us at this point in time.
if (valueAll == "true") {
while (len) {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
// Otherwise, just return one protobuf
} else {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
// the param "all" was not spcified. Return just one protobuf
} else {
len = webAPI.getFromRadio(txBuf);
res->write(txBuf, len);
}
DEBUG_MSG("--------------- webAPI handleAPIv1FromRadio, len %d\n", len);
}
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
{
DEBUG_MSG("+++++++++++++++ webAPI handleAPIv1ToRadio\n");
/*
For documentation, see:
https://github.com/meshtastic/Meshtastic-device/wiki/HTTP-REST-API-discussion
https://github.com/meshtastic/Meshtastic-device/blob/master/docs/software/device-api.md
Example:
http://10.10.30.198/api/v1/toradio
*/
// Status code is 200 OK by default.
res->setHeader("Content-Type", "application/x-protobuf");
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/Meshtastic-protobufs/master/mesh.proto");
if (req->getMethod() == "OPTIONS") {
res->setStatusCode(204); // Success with no content
res->print("");
return;
}
byte buffer[MAX_TO_FROM_RADIO_SIZE];
size_t s = req->readBytes(buffer, MAX_TO_FROM_RADIO_SIZE);
DEBUG_MSG("Received %d bytes from PUT request\n", s);
webAPI.handleToRadio(buffer, s);
res->write(buffer, s);
DEBUG_MSG("--------------- webAPI handleAPIv1ToRadio\n");
}
/*
To convert text to c strings:
https://tomeko.net/online_tools/cpp_text_escape.php?lang=en
*/
void handleRoot(HTTPRequest *req, HTTPResponse *res)
{
res->setHeader("Content-Type", "text/html");
res->setHeader("Set-Cookie",
"mt_session=" + httpsserver::intToString(random(1, 9999999)) + "; Expires=Wed, 20 Apr 2049 4:20:00 PST");
std::string cookie = req->getHeader("Cookie");
// String cookieString = cookie.c_str();
// uint8_t nameIndex = cookieString.indexOf("mt_session");
// DEBUG_MSG(cookie.c_str());
std::string filename = "/static/index.html";
std::string filenameGzip = "/static/index.html.gz";
if (!SPIFFS.exists(filename.c_str()) && !SPIFFS.exists(filenameGzip.c_str())) {
// Send "404 Not Found" as response, as the file doesn't seem to exist
res->setStatusCode(404);
res->setStatusText("Not found");
res->println("404 Not Found");
res->printf("<p>File not found: %s</p>\n", filename.c_str());
res->printf("<p></p>\n");
res->printf("<p>You have gotten this error because the filesystem for the web server has not been loaded.</p>\n");
res->printf("<p>Please review the 'Common Problems' section of the <a "
"href=https://github.com/meshtastic/Meshtastic-device/wiki/How-to-use-the-Meshtastic-Web-Interface-over-WiFi>web interface</a> documentation.</p>\n");
return;
}
// Try to open the file from SPIFFS
File file;
if (SPIFFS.exists(filename.c_str())) {
file = SPIFFS.open(filename.c_str());
if (!file.available()) {
DEBUG_MSG("File not available - %s\n", filename.c_str());
}
} else if (SPIFFS.exists(filenameGzip.c_str())) {
file = SPIFFS.open(filenameGzip.c_str());
res->setHeader("Content-Encoding", "gzip");
if (!file.available()) {
DEBUG_MSG("File not available\n");
}
}
// Read the file from SPIFFS and write it to the HTTP response body
size_t length = 0;
do {
char buffer[256];
length = file.read((uint8_t *)buffer, 256);
std::string bufferString(buffer, length);
res->write((uint8_t *)bufferString.c_str(), bufferString.size());
} while (length > 0);
}
void handleRestart(HTTPRequest *req, HTTPResponse *res)
{
res->setHeader("Content-Type", "text/html");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
DEBUG_MSG("***** Restarted on HTTP(s) Request *****\n");
res->println("Restarting");
@@ -1030,6 +997,8 @@ void handleRestart(HTTPRequest *req, HTTPResponse *res)
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
{
res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "POST");
ResourceParameters *params = req->getParams();
std::string blink_target;
@@ -1058,100 +1027,11 @@ void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
res->println("}");
}
void handleReport(HTTPRequest *req, HTTPResponse *res)
{
ResourceParameters *params = req->getParams();
std::string content;
if (!params->getQueryParameter("content", content)) {
content = "json";
}
if (content == "json") {
res->setHeader("Content-Type", "application/json");
} else {
res->setHeader("Content-Type", "text/html");
res->println("<pre>");
}
res->println("{");
res->println("\"data\": {");
res->println("\"airtime\": {");
uint16_t *logArray;
res->print("\"tx_log\": [");
logArray = airtimeReport(TX_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint16_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->print("\"rx_log\": [");
logArray = airtimeReport(RX_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint16_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->print("\"rx_all_log\": [");
logArray = airtimeReport(RX_ALL_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) {
uint16_t tmp;
tmp = *(logArray + i);
res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) {
res->print(", ");
}
}
res->println("],");
res->printf("\"seconds_since_boot\": %u,\n", getSecondsSinceBoot());
res->printf("\"seconds_per_period\": %u,\n", getSecondsPerPeriod());
res->printf("\"periods_to_log\": %u\n", getPeriodsToLog());
res->println("},");
res->println("\"wifi\": {");
res->println("\"rssi\": " + String(WiFi.RSSI()) + ",");
if (radioConfig.preferences.wifi_ap_mode || isSoftAPForced()) {
res->println("\"ip\": \"" + String(WiFi.softAPIP().toString().c_str()) + "\"");
} else {
res->println("\"ip\": \"" + String(WiFi.localIP().toString().c_str()) + "\"");
}
res->println("},");
res->println("\"test\": 123");
res->println("},");
res->println("\"status\": \"ok\"");
res->println("}");
}
void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
{
res->setHeader("Content-Type", "application/json");
res->setHeader("Access-Control-Allow-Origin", "*");
res->setHeader("Access-Control-Allow-Methods", "GET");
// res->setHeader("Content-Type", "text/html");
int n = WiFi.scanNetworks();
@@ -1191,22 +1071,3 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
res->println("\"status\": \"ok\"");
res->println("}");
}
void handleFavicon(HTTPRequest *req, HTTPResponse *res)
{
// Set Content-Type
res->setHeader("Content-Type", "image/vnd.microsoft.icon");
// Write data from header file
res->write(FAVICON_DATA, FAVICON_LENGTH);
}
void replaceAll(std::string &str, const std::string &from, const std::string &to)
{
if (from.empty())
return;
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
}

View File

@@ -0,0 +1,46 @@
#pragma once
void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer);
// Declare some handler functions for the various URLs on the server
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res);
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res);
void handleStyleCSS(HTTPRequest *req, HTTPResponse *res);
void handleHotspot(HTTPRequest *req, HTTPResponse *res);
void handleRoot(HTTPRequest *req, HTTPResponse *res);
void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res);
void handleStaticPost(HTTPRequest *req, HTTPResponse *res);
void handleStatic(HTTPRequest *req, HTTPResponse *res);
void handleRestart(HTTPRequest *req, HTTPResponse *res);
void handle404(HTTPRequest *req, HTTPResponse *res);
void handleFormUpload(HTTPRequest *req, HTTPResponse *res);
void handleScanNetworks(HTTPRequest *req, HTTPResponse *res);
void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res);
void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res);
void handleReport(HTTPRequest *req, HTTPResponse *res);
void handleFavicon(HTTPRequest *req, HTTPResponse *res);
void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
void middlewareSession(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
uint32_t getTimeSpeedUp();
void setTimeSpeedUp();
// Interface to the PhoneAPI to access the protobufs with messages
class HttpAPI : public PhoneAPI
{
public:
// Nothing here yet
private:
// Nothing here yet
protected:
// Nothing here yet
};

View File

@@ -0,0 +1,14 @@
#include "mesh/http/ContentHelper.h"
//#include <Arduino.h>
//#include "main.h"
void replaceAll(std::string &str, const std::string &from, const std::string &to)
{
if (from.empty())
return;
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
}
}

View File

@@ -0,0 +1,8 @@
#include <Arduino.h>
#include <functional>
#define BoolToString(x) ((x) ? "true" : "false")
void replaceAll(std::string &str, const std::string &from, const std::string &to);

View File

@@ -2,14 +2,7 @@
#include <functional>
/*
Steps:
- Compress the .js file to .js.gz
- Convert to hex:
http://tomeko.net/online_tools/file_to_hex.php?lang=en
- Paste into the array
- Note the filesize of your .gz file and write the file
size into the length int.
This file contains static content.
*/
// Length of the binary data

231
src/mesh/http/WebServer.cpp Normal file
View File

@@ -0,0 +1,231 @@
#include "mesh/http/WebServer.h"
#include "NodeDB.h"
#include "mesh/http/WiFiAPClient.h"
#include <HTTPBodyParser.hpp>
#include <HTTPMultipartBodyParser.hpp>
#include <HTTPURLEncodedBodyParser.hpp>
#include <WebServer.h>
#include <WiFi.h>
#ifndef NO_ESP32
#include "esp_task_wdt.h"
#endif
// Persistant Data Storage
#include <Preferences.h>
Preferences prefs;
/*
Including the esp32_https_server library will trigger a compile time error. I've
tracked it down to a reoccurrance of this bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57824
The work around is described here:
https://forums.xilinx.com/t5/Embedded-Development-Tools/Error-with-Standard-Libaries-in-Zynq/td-p/450032
Long story short is we need "#undef str" before including the esp32_https_server.
- Jm Casler (jm@casler.org) Oct 2020
*/
#undef str
// Includes for the https server
// https://github.com/fhessel/esp32_https_server
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>
#include <HTTPSServer.hpp>
#include <HTTPServer.hpp>
#include <SSLCert.hpp>
// The HTTPS Server comes in a separate namespace. For easier use, include it here.
using namespace httpsserver;
#include "mesh/http/ContentHandler.h"
SSLCert *cert;
HTTPSServer *secureServer;
HTTPServer *insecureServer;
bool isWebServerReady = 0;
bool isCertReady = 0;
void handleWebResponse()
{
if (isWifiAvailable() == 0) {
return;
}
if (isWebServerReady) {
// We're going to handle the DNS responder here so it
// will be ignored by the NRF boards.
handleDNSResponse();
secureServer->loop();
insecureServer->loop();
}
/*
Slow down the CPU if we have not received a request within the last few
seconds.
*/
if (millis() - getTimeSpeedUp() >= (25 * 1000)) {
setCpuFrequencyMhz(80);
setTimeSpeedUp();
}
}
void taskCreateCert(void *parameter)
{
prefs.begin("MeshtasticHTTPS", false);
// Delete the saved certs
if (0) {
DEBUG_MSG("Deleting any saved SSL keys ...\n");
// prefs.clear();
prefs.remove("PK");
prefs.remove("cert");
}
size_t pkLen = prefs.getBytesLength("PK");
size_t certLen = prefs.getBytesLength("cert");
DEBUG_MSG("Checking if we have a previously saved SSL Certificate.\n");
if (pkLen && certLen) {
DEBUG_MSG("Existing SSL Certificate found!\n");
} else {
DEBUG_MSG("Creating the certificate. This may take a while. Please wait...\n");
yield();
cert = new SSLCert();
yield();
int createCertResult = createSelfSignedCert(*cert, KEYSIZE_2048, "CN=meshtastic.local,O=Meshtastic,C=US",
"20190101000000", "20300101000000");
yield();
if (createCertResult != 0) {
DEBUG_MSG("Creating the certificate failed\n");
// Serial.printf("Creating the certificate failed. Error Code = 0x%02X, check SSLCert.hpp for details",
// createCertResult);
// while (true)
// delay(500);
} else {
DEBUG_MSG("Creating the certificate was successful\n");
DEBUG_MSG("Created Private Key: %d Bytes\n", cert->getPKLength());
// for (int i = 0; i < cert->getPKLength(); i++)
// Serial.print(cert->getPKData()[i], HEX);
// Serial.println();
DEBUG_MSG("Created Certificate: %d Bytes\n", cert->getCertLength());
// for (int i = 0; i < cert->getCertLength(); i++)
// Serial.print(cert->getCertData()[i], HEX);
// Serial.println();
prefs.putBytes("PK", (uint8_t *)cert->getPKData(), cert->getPKLength());
prefs.putBytes("cert", (uint8_t *)cert->getCertData(), cert->getCertLength());
}
}
isCertReady = 1;
vTaskDelete(NULL);
}
void createSSLCert()
{
if (isWifiAvailable() == 0) {
return;
}
// Create a new process just to handle creating the cert.
// This is a workaround for Bug: https://github.com/fhessel/esp32_https_server/issues/48
// jm@casler.org (Oct 2020)
xTaskCreate(taskCreateCert, /* Task function. */
"createCert", /* String with name of task. */
16384, /* Stack size in bytes. */
NULL, /* Parameter passed as input of the task */
16, /* Priority of the task. */
NULL); /* Task handle. */
DEBUG_MSG("Waiting for SSL Cert to be generated.\n");
while (!isCertReady) {
DEBUG_MSG(".");
delay(1000);
yield();
esp_task_wdt_reset();
}
DEBUG_MSG("SSL Cert Ready!\n");
}
WebServerThread *webServerThread;
WebServerThread::WebServerThread() : concurrency::OSThread("WebServerThread") {}
int32_t WebServerThread::runOnce()
{
// DEBUG_MSG("WebServerThread::runOnce()\n");
handleWebResponse();
// Loop every 5ms.
return (5);
}
void initWebServer()
{
DEBUG_MSG("Initializing Web Server ...\n");
prefs.begin("MeshtasticHTTPS", false);
size_t pkLen = prefs.getBytesLength("PK");
size_t certLen = prefs.getBytesLength("cert");
DEBUG_MSG("Checking if we have a previously saved SSL Certificate.\n");
if (pkLen && certLen) {
uint8_t *pkBuffer = new uint8_t[pkLen];
prefs.getBytes("PK", pkBuffer, pkLen);
uint8_t *certBuffer = new uint8_t[certLen];
prefs.getBytes("cert", certBuffer, certLen);
cert = new SSLCert(certBuffer, certLen, pkBuffer, pkLen);
DEBUG_MSG("Retrieved Private Key: %d Bytes\n", cert->getPKLength());
// DEBUG_MSG("Retrieved Private Key: " + String(cert->getPKLength()) + " Bytes");
// for (int i = 0; i < cert->getPKLength(); i++)
// Serial.print(cert->getPKData()[i], HEX);
// Serial.println();
DEBUG_MSG("Retrieved Certificate: %d Bytes\n", cert->getCertLength());
// for (int i = 0; i < cert->getCertLength(); i++)
// Serial.print(cert->getCertData()[i], HEX);
// Serial.println();
} else {
DEBUG_MSG("Web Server started without SSL keys! How did this happen?\n");
}
// We can now use the new certificate to setup our server as usual.
secureServer = new HTTPSServer(cert);
insecureServer = new HTTPServer();
registerHandlers(insecureServer, secureServer);
DEBUG_MSG("Starting Web Servers...\n");
secureServer->start();
insecureServer->start();
if (secureServer->isRunning() && insecureServer->isRunning()) {
DEBUG_MSG("HTTP and HTTPS Web Servers Ready! :-) \n");
isWebServerReady = 1;
} else {
DEBUG_MSG("HTTP and HTTPS Web Servers Failed! ;-( \n");
}
}

26
src/mesh/http/WebServer.h Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include "PhoneAPI.h"
#include "concurrency/OSThread.h"
#include <Arduino.h>
#include <functional>
void initWebServer();
void createSSLCert();
void handleWebResponse();
class WebServerThread : private concurrency::OSThread
{
public:
WebServerThread();
protected:
virtual int32_t runOnce();
};
extern WebServerThread *webServerThread;

View File

@@ -1,9 +1,9 @@
#include "meshwifi.h"
#include "mesh/http/WiFiAPClient.h"
#include "NodeDB.h"
#include "mesh/wifi/WiFiServerAPI.h"
#include "configuration.h"
#include "main.h"
#include "meshwifi/meshhttp.h"
#include "mesh/http/WebServer.h"
#include "mesh/wifi/WiFiServerAPI.h"
#include "target_specific.h"
#include <DNSServer.h>
#include <ESPmDNS.h>

View File

@@ -1,38 +0,0 @@
#pragma once
#include "PhoneAPI.h"
#include <Arduino.h>
#include <functional>
void initWebServer();
void createSSLCert();
void handleNotFound();
void handleWebResponse();
void handleJSONChatHistory();
void notifyWebUI();
void handleHotspot();
void handleStyleCSS();
void handleRoot();
void handleScriptsScriptJS();
void handleJSONChatHistoryDummy();
void replaceAll(std::string& str, const std::string& from, const std::string& to);
class HttpAPI : public PhoneAPI
{
public:
// Nothing here yet
private:
// Nothing here yet
protected:
// Nothing here yet
};

View File

@@ -7,13 +7,16 @@
#include "esp_bt.h"
#include "host/util/util.h"
#include "main.h"
#include "meshwifi/meshwifi.h"
#include "nimble/NimbleDefs.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include <Arduino.h>
#include <WiFi.h>
#ifndef NO_ESP32
#include "mesh/http/WiFiAPClient.h"
#endif
static bool pinShowing;
static uint32_t doublepressed;
@@ -545,7 +548,9 @@ void setBluetoothEnable(bool on)
if (firstTime) {
firstTime = 0;
} else {
initWifi(0);
#ifndef NO_ESP32
initWifi(0);
#endif
}
} else {
@@ -557,7 +562,9 @@ void setBluetoothEnable(bool on)
*/
// shutdown wifi
#ifndef NO_ESP32
deinitWifi();
#endif
// We have to totally teardown our bluetooth objects to prevent leaks
deinitBLE();

View File

@@ -1,5 +1,9 @@
#include "meshwifi/meshhttp.h"
#include "meshwifi/meshwifi.h"
//#include "mesh/wifi/WebServer.h"
#include "configuration.h"
#ifndef NO_ESP32
//#include "mesh/wifi/WiFiAPClient.h"
void initWifi(bool forceSoftAP) {}
@@ -10,7 +14,4 @@ bool isWifiAvailable()
return false;
}
void handleWebResponse() {}
/// Perform idle loop processing required by the wifi layer
void loopWifi() {}
#endif

View File

@@ -6,7 +6,7 @@
#include "configuration.h"
#include "main.h"
NodeInfoPlugin nodeInfoPlugin;
NodeInfoPlugin *nodeInfoPlugin;
bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, const User &p)
{

View File

@@ -29,4 +29,4 @@ class NodeInfoPlugin : public ProtobufPlugin<User>
virtual MeshPacket *allocReply();
};
extern NodeInfoPlugin nodeInfoPlugin;
extern NodeInfoPlugin *nodeInfoPlugin;

29
src/plugins/Plugins.cpp Normal file
View File

@@ -0,0 +1,29 @@
#include "plugins/NodeInfoPlugin.h"
#include "plugins/PositionPlugin.h"
#include "plugins/RemoteHardwarePlugin.h"
#include "plugins/ReplyPlugin.h"
#include "plugins/SerialPlugin.h"
#include "plugins/TextMessagePlugin.h"
/**
* Create plugin instances here. If you are adding a new plugin, you must 'new' it here (or somewhere else)
*/
void setupPlugins()
{
nodeInfoPlugin = new NodeInfoPlugin();
positionPlugin = new PositionPlugin();
textMessagePlugin = new TextMessagePlugin();
// Note: if the rest of meshtastic doesn't need to explicitly use your plugin, you do not need to assign the instance
// to a global variable.
new RemoteHardwarePlugin();
new ReplyPlugin();
#ifndef NO_ESP32
// Only run on an esp32 based device.
new SerialPlugin(); // Maintained by MC Hamster (Jm Casler) jm@casler.org
#endif
}

6
src/plugins/Plugins.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
/**
* Create plugin instances here. If you are adding a new plugin, you must 'new' it here (or somewhere else)
*/
void setupPlugins();

View File

@@ -5,7 +5,7 @@
#include "Router.h"
#include "configuration.h"
PositionPlugin positionPlugin;
PositionPlugin *positionPlugin;
bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position &p)
{
@@ -29,21 +29,10 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, const Position
MeshPacket *PositionPlugin::allocReply()
{
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
assert(node);
// We might not have a position yet for our local node, in that case, at least try to send the time
if(!node->has_position) {
memset(&node->position, 0, sizeof(node->position));
node->has_position = true;
}
NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position
assert(node->has_position);
Position &position = node->position;
// Update our local node info with our position (even if we don't decide to update anyone else)
position.time = getValidTime(RTCQualityGPS); // This nodedb timestamp might be stale, so update it if our clock is valid.
return allocDataProtobuf(position);
return allocDataProtobuf(node->position);
}
void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)

View File

@@ -30,4 +30,4 @@ class PositionPlugin : public ProtobufPlugin<Position>
virtual MeshPacket *allocReply();
};
extern PositionPlugin positionPlugin;
extern PositionPlugin *positionPlugin;

View File

@@ -6,8 +6,6 @@
#include "configuration.h"
#include "main.h"
RemoteHardwarePlugin remoteHardwarePlugin;
#define NUM_GPIOS 64
// Because (FIXME) we currently don't tell API clients status on sent messages

View File

@@ -5,9 +5,6 @@
#include <assert.h>
// Create an a static instance of our plugin - this registers with the plugin system
ReplyPlugin replyPlugin;
MeshPacket *ReplyPlugin::allocReply()
{
assert(currentRequest); // should always be !NULL

View File

@@ -0,0 +1,194 @@
#include "SerialPlugin.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include "configuration.h"
#include <Arduino.h>
#include <assert.h>
/*
SerialPlugin
A simple interface to send messages over the mesh network by sending strings
over a serial port.
Default is to use RX GPIO 16 and TX GPIO 17.
Need help with this plugin? Post your question on the Meshtastic Discourse:
https://meshtastic.discourse.group
Basic Usage:
1) Enable the plugin by setting serialplugin_enabled to 1.
2) Set the pins (serialplugin_rxd / serialplugin_rxd) for your preferred RX and TX GPIO pins.
On tbeam, recommend to use:
RXD 35
TXD 15
3) Set serialplugin_timeout to the amount of time to wait before we consider
your packet as "done".
4) (Optional) In SerialPlugin.h set the port to PortNum_TEXT_MESSAGE_APP if you want to
send messages to/from the general text message channel.
5) Connect to your device over the serial interface at 38400 8N1.
6) Send a packet up to 240 bytes in length. This will get relayed over the mesh network.
7) (Optional) Set serialplugin_echo to 1 and any message you send out will be echoed back
to your device.
TODO (in this order):
* Define a verbose RX mode to report on mesh and packet infomration.
- This won't happen any time soon.
KNOWN PROBLEMS
* Until the plugin is initilized by the startup sequence, the TX pin is in a floating
state. Device connected to that pin may see this as "noise".
* Will not work on NRF and the Linux device targets.
*/
#define RXD2 16
#define TXD2 17
#define SERIALPLUGIN_RX_BUFFER 128
#define SERIALPLUGIN_STRING_MAX Constants_DATA_PAYLOAD_LEN
#define SERIALPLUGIN_TIMEOUT 250
#define SERIALPLUGIN_BAUD 38400
#define SERIALPLUGIN_ACK 1
SerialPlugin *serialPlugin;
SerialPluginRadio *serialPluginRadio;
SerialPlugin::SerialPlugin() : concurrency::OSThread("SerialPlugin") {}
char serialStringChar[Constants_DATA_PAYLOAD_LEN];
int32_t SerialPlugin::runOnce()
{
#ifndef NO_ESP32
/*
Uncomment the preferences below if you want to use the plugin
without having to configure it from the PythonAPI or WebUI.
*/
// radioConfig.preferences.serialplugin_enabled = 1;
// radioConfig.preferences.serialplugin_rxd = 35;
// radioConfig.preferences.serialplugin_txd = 15;
// radioConfig.preferences.serialplugin_timeout = 1000;
// radioConfig.preferences.serialplugin_echo = 1;
if (radioConfig.preferences.serialplugin_enabled) {
if (firstTime) {
// Interface with the serial peripheral from in here.
DEBUG_MSG("Initializing serial peripheral interface\n");
if (radioConfig.preferences.serialplugin_rxd && radioConfig.preferences.serialplugin_txd) {
Serial2.begin(SERIALPLUGIN_BAUD, SERIAL_8N1, radioConfig.preferences.serialplugin_rxd,
radioConfig.preferences.serialplugin_txd);
} else {
Serial2.begin(SERIALPLUGIN_BAUD, SERIAL_8N1, RXD2, TXD2);
}
if (radioConfig.preferences.serialplugin_timeout) {
Serial2.setTimeout(
radioConfig.preferences.serialplugin_timeout); // Number of MS to wait to set the timeout for the string.
} else {
Serial2.setTimeout(SERIALPLUGIN_TIMEOUT); // Number of MS to wait to set the timeout for the string.
}
Serial2.setRxBufferSize(SERIALPLUGIN_RX_BUFFER);
serialPluginRadio = new SerialPluginRadio();
firstTime = 0;
} else {
String serialString;
while (Serial2.available()) {
serialString = Serial2.readString();
serialString.toCharArray(serialStringChar, Constants_DATA_PAYLOAD_LEN);
serialPluginRadio->sendPayload();
DEBUG_MSG("Received: %s\n", serialStringChar);
}
}
return (10);
} else {
DEBUG_MSG("Serial Plugin Disabled\n");
return (INT32_MAX);
}
#endif
}
MeshPacket *SerialPluginRadio::allocReply()
{
auto reply = allocDataPacket(); // Allocate a packet for sending
return reply;
}
void SerialPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
{
MeshPacket *p = allocReply();
p->to = dest;
p->decoded.want_response = wantReplies;
p->want_ack = SERIALPLUGIN_ACK;
p->decoded.data.payload.size = strlen(serialStringChar); // You must specify how many bytes are in the reply
memcpy(p->decoded.data.payload.bytes, serialStringChar, p->decoded.data.payload.size);
service.sendToMesh(p);
}
bool SerialPluginRadio::handleReceived(const MeshPacket &mp)
{
#ifndef NO_ESP32
if (radioConfig.preferences.serialplugin_enabled) {
auto &p = mp.decoded.data;
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
// nodeDB.getNodeNum(), mp.from, mp.to, mp.id, p.payload.size, p.payload.bytes);
if (mp.from == nodeDB.getNodeNum()) {
/*
* If radioConfig.preferences.serialplugin_echo is true, then echo the packets that are sent out back to the TX
* of the serial interface.
*/
if (radioConfig.preferences.serialplugin_echo) {
// For some reason, we get the packet back twice when we send out of the radio.
// TODO: need to find out why.
if (lastRxID != mp.id) {
lastRxID = mp.id;
// DEBUG_MSG("* * Message came this device\n");
// Serial2.println("* * Message came this device");
Serial2.printf("%s", p.payload.bytes);
}
}
} else {
// DEBUG_MSG("* * Message came from the mesh\n");
// Serial2.println("* * Message came from the mesh");
Serial2.printf("%s", p.payload.bytes);
}
} else {
DEBUG_MSG("Serial Plugin Disabled\n");
}
#endif
return true; // Let others look at this message also if they want
}

View File

@@ -0,0 +1,54 @@
#pragma once
#include "SinglePortPlugin.h"
#include "concurrency/OSThread.h"
#include "configuration.h"
#include <Arduino.h>
#include <functional>
class SerialPlugin : private concurrency::OSThread
{
bool firstTime = 1;
public:
SerialPlugin();
protected:
virtual int32_t runOnce();
};
extern SerialPlugin *serialPlugin;
/*
* Radio interface for SerialPlugin
*
*/
class SerialPluginRadio : public SinglePortPlugin
{
uint32_t lastRxID;
public:
/*
TODO: Switch this to PortNum_SERIAL_APP once the change is able to be merged back here
from the main code.
*/
// SerialPluginRadio() : SinglePortPlugin("SerialPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
SerialPluginRadio() : SinglePortPlugin("SerialPluginRadio", PortNum_SERIAL_APP) {}
/**
* Send our payload into the mesh
*/
void sendPayload(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
protected:
virtual MeshPacket *allocReply();
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceived(const MeshPacket &mp);
};
extern SerialPluginRadio *serialPluginRadio;

View File

@@ -3,7 +3,7 @@
#include "NodeDB.h"
#include "PowerFSM.h"
TextMessagePlugin textMessagePlugin;
TextMessagePlugin *textMessagePlugin;
bool TextMessagePlugin::handleReceived(const MeshPacket &mp)
{

View File

@@ -22,4 +22,4 @@ class TextMessagePlugin : public SinglePortPlugin, public Observable<const MeshP
virtual bool handleReceived(const MeshPacket &mp);
};
extern TextMessagePlugin textMessagePlugin;
extern TextMessagePlugin *textMessagePlugin;

View File

@@ -1,4 +1,4 @@
[VERSION]
major = 1
minor = 1
build = 32
build = 33