mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-11 20:37:22 +00:00
Compare commits
159 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abb52e5446 | ||
|
|
de37a0c31e | ||
|
|
6e31ba30c7 | ||
|
|
8fe1c518d9 | ||
|
|
b6006fe3d5 | ||
|
|
3e8173c4bd | ||
|
|
d8a15d6324 | ||
|
|
b75c7ad179 | ||
|
|
44f89c969d | ||
|
|
c0e0e095c9 | ||
|
|
6c1c0640f2 | ||
|
|
698102371f | ||
|
|
997ed283bf | ||
|
|
9128f7d4b3 | ||
|
|
93d0257be7 | ||
|
|
adc71e7ed2 | ||
|
|
516e18ca80 | ||
|
|
4777e53c23 | ||
|
|
d6912cfd8e | ||
|
|
621306e610 | ||
|
|
0e507e1923 | ||
|
|
15a0b3694d | ||
|
|
6e4cf22cf0 | ||
|
|
58859848a3 | ||
|
|
55f61826bf | ||
|
|
f80d357b77 | ||
|
|
c972197643 | ||
|
|
c06b7b2b48 | ||
|
|
3c69beef94 | ||
|
|
e55c5e10bc | ||
|
|
e321528a6d | ||
|
|
ee897bce6c | ||
|
|
186a52172c | ||
|
|
21570fc24f | ||
|
|
2edc6b363d | ||
|
|
0c74303e9d | ||
|
|
244e597a9f | ||
|
|
15833e1e53 | ||
|
|
73d64d378a | ||
|
|
8d04410f45 | ||
|
|
36d28d2da6 | ||
|
|
4a653ab054 | ||
|
|
651bd71454 | ||
|
|
1e9ebbc476 | ||
|
|
27c16ba185 | ||
|
|
51a8c7118a | ||
|
|
808c4ff5ca | ||
|
|
ded2b86e55 | ||
|
|
9efcdc7c67 | ||
|
|
34e6dbec81 | ||
|
|
62b655ccea | ||
|
|
3c2aac87f7 | ||
|
|
3aba097096 | ||
|
|
f45451ca74 | ||
|
|
c35fec9f20 | ||
|
|
88fa24ce79 | ||
|
|
59577b9d79 | ||
|
|
c349ad62e7 | ||
|
|
d5b57840d9 | ||
|
|
f8a3d143cb | ||
|
|
c717dfc33d | ||
|
|
22d9096c3d | ||
|
|
8080bc608b | ||
|
|
c4b9d60afa | ||
|
|
dda5568e2c | ||
|
|
2d8e00e2a0 | ||
|
|
901ff6bb1e | ||
|
|
7312c56d6c | ||
|
|
031c58e21c | ||
|
|
35b1cfcc42 | ||
|
|
e545778154 | ||
|
|
6fd2bc5f83 | ||
|
|
9a587b2743 | ||
|
|
bacc1b1dad | ||
|
|
575b69c541 | ||
|
|
73092b4b40 | ||
|
|
877dc824a9 | ||
|
|
89c76dca11 | ||
|
|
2253ea1b41 | ||
|
|
b732a13d6c | ||
|
|
8e0c224813 | ||
|
|
5a96dc0083 | ||
|
|
181db06b0c | ||
|
|
47ccfb6106 | ||
|
|
5f97740ab7 | ||
|
|
90d6878bbb | ||
|
|
5c70f36aa5 | ||
|
|
09cc0a85db | ||
|
|
a47fcdacb5 | ||
|
|
ef0891ae5d | ||
|
|
a8d7700295 | ||
|
|
412916ba7c | ||
|
|
616290edcc | ||
|
|
9ed19892e2 | ||
|
|
88cf60ad9d | ||
|
|
7f59e76c72 | ||
|
|
dcb9125b32 | ||
|
|
2743b9d310 | ||
|
|
2f779bfd37 | ||
|
|
db2193b526 | ||
|
|
7205e9a5b4 | ||
|
|
1ca83509dd | ||
|
|
7135a12300 | ||
|
|
fae9ea8b3b | ||
|
|
b96ee7be72 | ||
|
|
9e449bebf9 | ||
|
|
e32202e4f8 | ||
|
|
ca99b6b3b7 | ||
|
|
2eb2e9142f | ||
|
|
15e1a3870c | ||
|
|
5bdc7216b3 | ||
|
|
cc127f7dad | ||
|
|
be38a58a62 | ||
|
|
5930f8270d | ||
|
|
c9f2318e78 | ||
|
|
5cdc2f5142 | ||
|
|
53d773b81f | ||
|
|
85b2ba7ce9 | ||
|
|
474e0e7158 | ||
|
|
99a8c80c44 | ||
|
|
03a9d7da5e | ||
|
|
6975848f45 | ||
|
|
0cdc1fc959 | ||
|
|
e80c79edbe | ||
|
|
651d045afe | ||
|
|
86952c5456 | ||
|
|
46781357df | ||
|
|
bb9abf2dca | ||
|
|
5249608dce | ||
|
|
ee8f4de5ab | ||
|
|
17297db2b1 | ||
|
|
ad8bcba5ef | ||
|
|
138cebbf03 | ||
|
|
9f9573d2eb | ||
|
|
e10b82c118 | ||
|
|
d82aaaa806 | ||
|
|
c0d94ae4ab | ||
|
|
02ce12607c | ||
|
|
bdeba54c50 | ||
|
|
26c9585c9d | ||
|
|
696255c1f7 | ||
|
|
d857f8ba6d | ||
|
|
5852caa61c | ||
|
|
e82752c777 | ||
|
|
3eae2c6286 | ||
|
|
1e5d0b25ad | ||
|
|
c8423400ea | ||
|
|
af88a34f75 | ||
|
|
b9f1ce70cb | ||
|
|
c361c1fab7 | ||
|
|
091e953ed4 | ||
|
|
9ab02119f5 | ||
|
|
2d4849e0d0 | ||
|
|
a70cda6fe4 | ||
|
|
8bdbbfbe16 | ||
|
|
0ba4925f75 | ||
|
|
26d50fda9a | ||
|
|
bc22ab7b87 | ||
|
|
d5e3e63d6d |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -17,5 +17,5 @@ Thumbs.db
|
||||
.cproject
|
||||
.idea/*
|
||||
.vagrant
|
||||
|
||||
nanopb*
|
||||
flash.uf2
|
||||
|
||||
12
README.md
12
README.md
@@ -30,11 +30,14 @@ We currently support three models of radios.
|
||||
- [T-Beam V0.7 w/ NEO-6M](https://www.aliexpress.com/item/4000574335430.html) (will work but **you must use the tbeam0.7 firmware ** - but the T-Beam V1.0 or later are better!)
|
||||
- board labels "TTGO T22_V07 20180711"
|
||||
- 3D printable cases
|
||||
- [T-Beam V0](https://www.thingiverse.com/thing:3773717)
|
||||
- [T-Beam V0](https://www.thingiverse.com/thing:3773717) (GPS and LoRa antenna misaligned if GPS placed as pictured)
|
||||
- [T-Beam V1 (SMA-antenna)](https://www.thingiverse.com/thing:3830711)
|
||||
- [T-Beam V1 (IPEX-antenna)](https://www.thingiverse.com/thing:4587297)
|
||||
- [T-Beam V1 (SMA-antenna)](https://www.thingiverse.com/thing:4677388) (Mounting option for larger GPS antenna but LoRa antenna enclosed)
|
||||
- [T-Beam V1 (IPEX-antenna)](https://www.thingiverse.com/thing:4587297) (GPS and LoRa antenna misaligned if GPS placed as pictured)
|
||||
- [T-Beam V1 (IPEX-antenna)](https://www.thingiverse.com/thing:4589651)
|
||||
- [T-Beam V1 (IPEX-antenna)](https://www.thingiverse.com/thing:4619981) (GPS and LoRa antenna misaligned if GPS placed as pictured)
|
||||
- Laser-cut cases
|
||||
- [T-Beam V1](https://www.thingiverse.com/thing:4552771)
|
||||
- [T-Beam V1 (SMA-antenna)](https://www.thingiverse.com/thing:4552771)
|
||||
|
||||
- [TTGO LORA32](https://www.aliexpress.com/item/4000211331316.html) - No GPS
|
||||
- version 2.1
|
||||
@@ -43,7 +46,10 @@ We currently support three models of radios.
|
||||
- [TTGO LORA32 v1](https://www.thingiverse.com/thing:3385109)
|
||||
|
||||
- [Heltec LoRa 32](https://heltec.org/project/wifi-lora-32/) - No GPS
|
||||
- [Official Heltec case](https://www.aliexpress.com/item/4001050707951.html)
|
||||
- [3D Printable case](https://www.thingiverse.com/thing:3125854)
|
||||
|
||||
Note: The GPS and LoRa stock antennas should be placed in a way, that the GPS antenna faces the sky and the LoRa antenna radiates 360 degrees horizontally. For better GPS reception you might want to [upgrade the GPS antenna](https://meshtastic.discourse.group/t/the-importance-of-gps-antennas-and-request-to-3d-case-documentation-people/1505) and to properly align the antennas you might want to upgrade to a LoRa antenna that can be adjusted to radiate into the right directions.
|
||||
|
||||
**Make sure to get the frequency for your country**
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ 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
|
||||
|
||||
OUTDIR=release/latest
|
||||
|
||||
# We keep all old builds (and their map files in the archive dir)
|
||||
@@ -49,7 +51,7 @@ function do_build() {
|
||||
basename=universal/firmware-$BOARD-$VERSION
|
||||
fi
|
||||
|
||||
pio run --jobs 4 --environment $BOARD # -v
|
||||
pio run --jobs $NUM_JOBS --environment $BOARD # -v
|
||||
SRCELF=.pio/build/$BOARD/firmware.elf
|
||||
cp $SRCELF $OUTDIR/elfs/$basename.elf
|
||||
|
||||
|
||||
@@ -3,19 +3,14 @@ Import("projenv")
|
||||
|
||||
import configparser
|
||||
prefsLoc = projenv["PROJECT_DIR"] + "/version.properties"
|
||||
print(f"Preferences in {prefsLoc}")
|
||||
try:
|
||||
config = configparser.RawConfigParser()
|
||||
config.read(prefsLoc)
|
||||
version = dict(config.items('VERSION'))
|
||||
verStr = "{}.{}.{}".format(version["major"], version["minor"], version["build"])
|
||||
except:
|
||||
print("Can't read preferences, using 0.0.0")
|
||||
verStr = "0.0.0"
|
||||
config = configparser.RawConfigParser()
|
||||
config.read(prefsLoc)
|
||||
version = dict(config.items('VERSION'))
|
||||
verStr = "{}.{}.{}".format(version["major"], version["minor"], version["build"])
|
||||
|
||||
print(f"Using meshtastic platform-custom.py, firmare version {verStr}")
|
||||
print("Using meshtastic platform-custom.py, firmare version " + verStr)
|
||||
|
||||
# General options that are passed to the C and C++ compilers
|
||||
projenv.Append(CCFLAGS=[
|
||||
f"-DAPP_VERSION={verStr}"
|
||||
"-DAPP_VERSION=" + verStr
|
||||
])
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
set -e
|
||||
|
||||
source bin/version.sh
|
||||
VERSION=`bin/buildinfo.py`
|
||||
|
||||
esptool.py --baud 921600 write_flash 0x10000 release/latest/bins/firmware-tbeam-US-$VERSION.bin
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
|
||||
set -e
|
||||
|
||||
echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.1"
|
||||
echo "This script requires https://jpa.kapsi.fi/nanopb/download/ version 0.4.4 to be located in the"
|
||||
echo "meshtastic-device root directory if the following step fails, you should download the correct"
|
||||
echo "prebuilt binaries for your computer into nanopb-0.4.4"
|
||||
|
||||
# the nanopb tool seems to require that the .options file be in the current directory!
|
||||
cd proto
|
||||
../../nanopb-0.4.1-linux-x86/generator-bin/protoc --nanopb_out=-v:../src/mesh -I=../proto *.proto
|
||||
../nanopb-0.4.4/generator-bin/protoc --nanopb_out=-v:../src/mesh/generated -I=../proto *.proto
|
||||
|
||||
echo "Regenerating protobuf documentation - if you see an error message"
|
||||
echo "you can ignore it unless doing a new protobuf release to github."
|
||||
|
||||
@@ -4,17 +4,21 @@ You probably don't care about this section - skip to the next one.
|
||||
|
||||
For app cleanup:
|
||||
|
||||
* use structured logging to kep logs in ram. Also send logs as packets to api clients
|
||||
* DONE writeup nice python options docs (common cases, link to protobuf docs)
|
||||
* have android app link to user manual
|
||||
* DONE only do wantReplies once per packet type, if we change network settings force it again
|
||||
* update positions and nodeinfos based on packets we just merely witness on the mesh. via isPromsciousPort bool, remove sniffing
|
||||
* DONE make device build always have a valid version
|
||||
* DONE do fixed position bug https://github.com/meshtastic/Meshtastic-device/issues/536
|
||||
* check build guide
|
||||
* generate autodocs
|
||||
* write devapi user guide
|
||||
* DONE check build guide
|
||||
* DONE write devapi user guide
|
||||
* DONE update android code: https://developer.android.com/topic/libraries/view-binding/migration
|
||||
* only do wantReplies once per packet type, if we change network settings force it again
|
||||
* make gpio watch work, use thread and setup
|
||||
* make hello world example service
|
||||
* make python ping command
|
||||
* make python gpio read a bit cleaner
|
||||
* DONE test GPIO watch
|
||||
* DONE set --set-chan-fast, --set-chan-default
|
||||
* writeup docs on gpio
|
||||
* DONE make python ping command
|
||||
* DONE make hello world example service
|
||||
* DONE have python tool check max packet size before sending to device
|
||||
* DONE if request was sent reliably, send reply reliably
|
||||
* DONE require a recent python api to talk to these new device loads
|
||||
@@ -22,8 +26,8 @@ For app cleanup:
|
||||
* DONE fix handleIncomingPosition
|
||||
* DONE move want_replies handling into plugins
|
||||
* DONE on android for received positions handle either old or new positions / user messages
|
||||
* on android side send old or new positions as needed / user messages
|
||||
* test python side handle new position/user messages
|
||||
* DONE on android side send old or new positions as needed / user messages
|
||||
* DONE test python side handle new position/user messages
|
||||
* DONE make a gpio example. --gpiowrb 4 1, --gpiord 0x444, --gpiowatch 0x3ff
|
||||
* DONE fix position sending to use new plugin
|
||||
* DONE Add SinglePortNumPlugin - as the new most useful baseclass
|
||||
@@ -31,9 +35,11 @@ For app cleanup:
|
||||
* DONE move user info into regular data packets (use new app framework)
|
||||
* DONE test that positions, text messages and user info still work
|
||||
* DONE test that position, text messages and user info work properly with new android app and old device code
|
||||
* fix the RTC drift bug
|
||||
* DONE do UDP tunnel
|
||||
* DONE fix the RTC drift bug
|
||||
* move python ping functionality into device, reply with rxsnr info
|
||||
* use channels for gpio security https://github.com/meshtastic/Meshtastic-device/issues/104
|
||||
* MeshPackets for sending should be reference counted so that API clients would have the option of checking sent status (would allow removing the nasty 30 sec timer in gpio watch sending)
|
||||
|
||||
For high speed/lots of devices/short range tasks:
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# Build instructions
|
||||
|
||||
This project uses the simple PlatformIO build system. PlatformIO is an extension to Microsoft VSCode.
|
||||
This project uses the simple PlatformIO build system. PlatformIO is an extension to Microsoft VSCode. Workflows from building from the GUI or from the commandline are listed below.
|
||||
|
||||
If you encounter any problems, please post a question in [our forum](meshtastic.discourse.group). And when you learn a fix, update these instructions for the next person (i.e. edit this file and send in a [pull-request](https://opensource.com/article/19/7/create-pull-request-github) which we will eagerly merge).
|
||||
|
||||
## GUI
|
||||
|
||||
1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information).
|
||||
2. Install [Python](https://www.python.org/downloads/).
|
||||
3. Install [Git](https://git-scm.com/downloads).
|
||||
@@ -19,6 +22,7 @@ This project uses the simple PlatformIO build system. PlatformIO is an extension
|
||||
Note - To get a clean build you may have to delete the auto-generated file `./.vscode/c_cpp_properties.json`, close and re-open Visual Studio and WAIT until the file is auto-generated before compiling again.
|
||||
|
||||
## Command Line
|
||||
|
||||
1. Purchase a suitable [radio](https://github.com/meshtastic/Meshtastic-device/wiki/Hardware-Information).
|
||||
2. Install [PlatformIO](https://platformio.org/platformio-ide)
|
||||
3. Download this git repo and cd into it:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# Device API
|
||||
# Bluetooth/serial/TCP protocol API
|
||||
|
||||
(This document describes the protocol for external API clients using our devices. If you are interested in running your own code on the device itself, see the [on-device](plugin-api.md) documentation instead)
|
||||
|
||||
The Device API is design to have only a simple stream of ToRadio and FromRadio packets and all polymorphism comes from the flexible set of Google Protocol Buffers which are sent over the wire. We use protocol buffers extensively both for the bluetooth API and for packets inside the mesh or when providing packets to other applications on the phone.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ you'll automatically get our fixed libraries.
|
||||
IDF release/v3.3 46b12a560
|
||||
IDF release/v3.3 367c3c09c
|
||||
https://docs.espressif.com/projects/esp-idf/en/release-v3.3/get-started/linux-setup.html
|
||||
kevinh@kevin-server:~/development/meshtastic/esp32-arduino-lib-builder\$ python /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/esp-idf/components/esptool*py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/build/bootloader/bootloader.bin
|
||||
kevinh@kevin-server:~/development/meshtastic/esp32-arduino-lib-builder\$ python /home/kevinh/development/meshtastic/
|
||||
cp -a out/tools/sdk/* components/arduino/tools/sdk
|
||||
cp -ar components/arduino/* ~/.platformio/packages/framework-arduinoespressif32
|
||||
|
||||
@@ -21,3 +21,9 @@ you'll automatically get our fixed libraries.
|
||||
cp -ar out/tools/sdk/* ~/.platformio/packages/framework-arduinoespressif32/tools/sdk
|
||||
|
||||
```
|
||||
|
||||
How to flash new bootloader
|
||||
|
||||
```
|
||||
esp32-arduino-lib-builder/esp-idf/components/esptool*py/esptool/esptool.py --chip esp32 --port /dev/ttyUSB0 --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dout --flash_freq 40m --flash_size detect 0x1000 /home/kevinh/development/meshtastic/esp32-arduino-lib-builder/build/bootloader/bootloader.bin
|
||||
```
|
||||
|
||||
192
docs/software/mqtt.md
Normal file
192
docs/software/mqtt.md
Normal file
@@ -0,0 +1,192 @@
|
||||
|
||||
# Table of Contents
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Abstract](#abstract)
|
||||
- [Short term goals](#short-term-goals)
|
||||
- [Long term goals](#long-term-goals)
|
||||
- [Multiple Channel support / Security](#multiple-channel-support--security)
|
||||
- [On device API](#on-device-api)
|
||||
- [MQTT transport](#mqtt-transport)
|
||||
- [Topics](#topics)
|
||||
- [Service Envelope](#service-envelope)
|
||||
- [NODEID](#nodeid)
|
||||
- [USERID](#userid)
|
||||
- [CHANNELID](#channelid)
|
||||
- [Gateway nodes](#gateway-nodes)
|
||||
- [Optional web services](#optional-web-services)
|
||||
- [Public MQTT broker service](#public-mqtt-broker-service)
|
||||
- [Riot.im messaging bridge](#riotim-messaging-bridge)
|
||||
- [Deprecated concepts](#deprecated-concepts)
|
||||
- [MESHID (deprecated)](#meshid-deprecated)
|
||||
- [DESTCLASS (deprecated)](#destclass-deprecated)
|
||||
- [DESTID (deprecated)](#destid-deprecated)
|
||||
- [Rejected idea: RAW UDP](#rejected-idea-raw-udp)
|
||||
- [Development plan](#development-plan)
|
||||
- [Work items](#work-items)
|
||||
- [Enhancements in following releases](#enhancements-in-following-releases)
|
||||
|
||||
## Abstract
|
||||
|
||||
This is a mini-doc/RFC sketching out a development plan to satisfy a number of 1.1 goals.
|
||||
|
||||
- [MQTT](https://opensource.com/article/18/6/mqtt) internet accessible API. Issue #[369](https://github.com/meshtastic/Meshtastic-device/issues/169)
|
||||
- An open API to easily run custom mini-apps on the devices
|
||||
- A text messaging bridge when a node in the mesh can gateway to the internet. Issue #[353](https://github.com/meshtastic/Meshtastic-device/issues/353) and this nicely documented [android issue](https://github.com/meshtastic/Meshtastic-Android/issues/2).
|
||||
- An easy way to let desktop app developers remotely control GPIOs. Issue #[182](https://github.com/meshtastic/Meshtastic-device/issues/182)
|
||||
- Remote attribute access (to change settings of distant nodes). Issue #182
|
||||
|
||||
## Short term goals
|
||||
|
||||
- We want a clean API for novice developers to write mini "apps" that run **on the device** with the existing messaging/location "apps".
|
||||
- We want the ability to have a gateway web service, so that if any node in the mesh can connect to the internet (via its connected phone app or directly) then that node will provide bidirectional messaging between nodes and the internet.
|
||||
- We want an easy way for novice developers to remotely read and control GPIOs (because this is an often requested use case), without those developers having to write any device code.
|
||||
- We want a way to gateway text messaging between our current private meshes and the broader internet (when that mesh is able to connect to the internet)
|
||||
- We want a way to remotely set any device/channel parameter on a node. This is particularly important for administering physically inaccessible router nodes. Ideally this mechanism would also be used for administering the local node (so one common mechanism for both cases).
|
||||
- This work should be independent of our current (semi-custom) LoRa transport, so that in the future we can swap out that transport if we wish (to QMesh or Reticulum?)
|
||||
- Our networks are (usually) very slow and low bandwidth, so the messaging must be very airtime efficient.
|
||||
|
||||
## Long term goals
|
||||
|
||||
- Store and forward messaging should be supported, so apps can send messages that might be delivered to their destination in **hours** or **days** if a node/mesh was partitioned.
|
||||
|
||||
## Multiple Channel support / Security
|
||||
|
||||
Mini-apps API can bind to particular channels. They will only see messages sent on that channel.
|
||||
|
||||
During the 1.0 timeframe only one channel was supported per node. Starting in the 1.1 tree we will do things like "remote admin operations / channel settings etc..." are on the "Control" channel and only especially trusted users should have the keys to access that channel.
|
||||
|
||||
FIXME - explain this more, talk about how useful for users and security domains.
|
||||
- add channels as security
|
||||
|
||||
## On device API
|
||||
|
||||
For information on the related on-device API see [here](device-api.md).
|
||||
|
||||
## MQTT transport
|
||||
|
||||
Any gateway-device will contact the MQTT broker.
|
||||
|
||||
### Topics
|
||||
|
||||
The "mesh/crypt/CHANNELID/NODEID/PORTID" [topic](https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices/) will be used for messages sent from/to a mesh.
|
||||
|
||||
Gateway nodes will foward any MeshPacket from a local mesh channel with uplink_enabled. The packet (encapsulated in a ServiceEnvelope) will remain encrypted with the key for the specified channel.
|
||||
|
||||
For any channels in the local node with downlink_enabled, the gateway node will forward packets from MQTT to the local mesh. It will do this by subscribing to mesh/crypt/CHANNELID/# and forwarding relevant packets.
|
||||
|
||||
If the channelid 'well known'/public it could be decrypted by a web service (if the web service was provided with the associated channel key), in which case it will be decrypted by a web service and appear at "mesh/clear/CHANNELID/NODEID/PORTID". Note: This is not in the initial deliverable.
|
||||
|
||||
FIXME, discuss how text message global mirroring could scale (or not)
|
||||
FIXME, possibly don't global mirror text messages - instead rely on matrix/riot?
|
||||
FIXME, discuss possible attacks by griefers and how they can be prvented
|
||||
|
||||
#### Service Envelope
|
||||
|
||||
The payload published on mesh/... will always be wrapped in a [ServiceEnvelope protobuf](https://github.com/meshtastic/Meshtastic-protobufs/blob/master/docs/docs.md#.ServiceEnvelope).
|
||||
|
||||
ServiceEnvelope will include the message, and full information about arrival time, who forwarded it, source channel, source mesh id, etc...
|
||||
|
||||
#### NODEID
|
||||
|
||||
The unique ID for a node. A hex string that starts with a ! symbol.
|
||||
|
||||
#### USERID
|
||||
|
||||
A user ID string. This string is either a user ID if known or a nodeid to simply deliver the message to whoever the local user is of a particular device (i.e. person who might see the screen). FIXME, see what riot.im uses and perhaps use that convention? Or use the signal +phone number convention? Or the email addr?
|
||||
|
||||
#### CHANNELID
|
||||
|
||||
FIXME, figure out how channelids work
|
||||
|
||||
### Gateway nodes
|
||||
|
||||
Any meshtastic node that has a direct connection to the internet (either via a helper app or installed wifi/4G/satellite hardware) can function as a "Gateway node".
|
||||
|
||||
Gateway nodes (via code running in the phone) will contain two tables to whitelist particular traffic to either be delivered toward the internet, or down toward the mesh. Users that are developing custom apps will be able to customize these filters/subscriptions.
|
||||
|
||||
Since multiple gateway nodes might be connected to a single mesh, it is possible that duplicate messages will be published on any particular topic. Therefore subscribers to these topics should
|
||||
deduplicate if needed by using the packet ID of each message.
|
||||
|
||||
### Optional web services
|
||||
|
||||
#### Public MQTT broker service
|
||||
|
||||
An existing public [MQTT broker](https://mosquitto.org/) will be the default for this service, but clients can use any MQTT broker they choose.
|
||||
|
||||
FIXME - figure out how to avoid impersonation (because we are initially using a public mqtt server with no special security options). FIXME, include some ideas on this in the ServiceEnvelope documentation.
|
||||
|
||||
#### Riot.im messaging bridge
|
||||
|
||||
@Geeksville will run a riot.im bridge that talks to the public MQTT broker and sends/receives into the riot.im network.
|
||||
|
||||
There is apparently [already](https://github.com/derEisele/tuple) a riot.im [bridge](https://matrix.org/bridges/) for MQTT. That will possibly need to be customized a bit. But by doing this, we should be able to let random riot.im users send/receive messages to/from any meshtastic device. (FIXME ponder security). See this [issue](https://github.com/meshtastic/Meshtastic-Android/issues/2#issuecomment-645660990) with discussion with the dev.
|
||||
|
||||
### Deprecated concepts
|
||||
|
||||
You can ignore these for now...
|
||||
|
||||
#### MESHID (deprecated)
|
||||
|
||||
Earlier drafts of this document included the concept of a MESHID. That concept has been removed for now, but might be useful in the future. The old idea is listed below:
|
||||
|
||||
A unique ID for this mesh. There will be some sort of key exchange process so that the mesh ID can not be impersonated by other meshes.
|
||||
|
||||
#### DESTCLASS (deprecated)
|
||||
|
||||
Earlier drafts of this document included the concept of a DESTCLASS. That concept has been removed for now, but might be useful in the future. The old idea is listed below:
|
||||
|
||||
The type of DESTID this message should be delivered to. A short one letter sequence:
|
||||
|
||||
| Symbol | Description |
|
||||
| ------ | ------------------------------------------------------------- |
|
||||
| R | riot.im |
|
||||
| L | local mesh node ID or ^all |
|
||||
| A | an application specific message, ID will be an APP ID |
|
||||
| S | SMS gateway, DESTID is a phone number to reach via Twilio.com |
|
||||
| E | Emergency message, see bug #fixme for more context |
|
||||
|
||||
#### DESTID (deprecated)
|
||||
|
||||
Earlier drafts of this document included the concept of a DESTCLASS. That concept has been removed for now, but might be useful in the future. The old idea is listed below:
|
||||
|
||||
Can be...
|
||||
|
||||
- an internet username: kevinh@geeksville.com
|
||||
- ^ALL for anyone
|
||||
- An app ID (to allow apps out in the web to receive arbitrary binary data from nodes or simply other apps using meshtastic as a transport). They would connect to the MQTT broker and subscribe to their topic
|
||||
|
||||
## Rejected idea: RAW UDP
|
||||
|
||||
A number of commenters have requested/proposed using UDP for the transport. We've considered this option and decided to use MQTT instead for the following reasons:
|
||||
|
||||
- Most UDP uses cases would need to have a server anyways so that nodes can reach each other from anywhere (i.e. if most gateways will be behind some form of NAT which would need to be tunnelled)
|
||||
- Raw UDP is dropped **very** agressively by many cellular providers. MQTT from the gateway to a broker can be done over a TCP connection for this reason.
|
||||
- MQTT provides a nice/documented/standard security model to build upon
|
||||
- MQTT is fairly wire efficient with multiple broker implementations/providers and numerous client libraries for any language. The actual implementation of MQTT is quite simple.
|
||||
|
||||
## Development plan
|
||||
|
||||
Given the previous problem/goals statement, here's the initial thoughts on the work items required. As this idea becomes a bit more fully baked we should add details
|
||||
on how this will be implemented and guesses at approximate work items.
|
||||
|
||||
### Work items
|
||||
|
||||
- Change nodeIDs to be base64 instead of eight hex digits.
|
||||
- DONE Refactor the position features into a position "mini-app". Use only the new public on-device API to implement this app.
|
||||
- DONE Refactor the on device texting features into a messaging "mini-app". (Similar to the position mini-app)
|
||||
- Add new multi channel concept
|
||||
- Send new channels to python client
|
||||
- Let python client add channels
|
||||
- Add portion of channelid to the raw lora packet header
|
||||
- Confirm that we can now forward encrypted packets without decrypting at each node
|
||||
- Use a channel named "remotehw" to secure the GPIO service. If that channel is not found, don't even start the service. Document this as the standard method for securing services.
|
||||
- Add first cut of the "gateway node" code (i.e. MQTT broker client) to the python API (very little code needed for this component)
|
||||
- Confirm that texting works to/from the internet
|
||||
- Confirm that positions are optionally sent to the internet
|
||||
- Add the first cut of the "gateway node" code to the android app (very little code needed for this component)
|
||||
|
||||
### Enhancements in following releases
|
||||
|
||||
The initial gateway will be added to the python tool. But the gateway implementation is designed to be fairly trivial/dumb. After the initial release the actual gateway code can be ported to also run inside of the android app. In fact, we could have ESP32 based nodes include a built-in "gateway node" implementation.
|
||||
|
||||
Store and forward could be added so that nodes on the mesh could deliver messages (i.e. text messages) on an "as possible" basis. This would allow things like "hiker sends a message to friend - mesh can not currently reach friend - eventually (days later) mesh can somehow reach friend, message gets delivered"
|
||||
76
docs/software/plugin-api.md
Normal file
76
docs/software/plugin-api.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Plugin-API
|
||||
|
||||
This is a tutorial on how to write small plugins which run on the device. Plugins are bits regular 'arduino' code that can send and receive packets to other nodes/apps/PCs using our mesh.
|
||||
|
||||
## Key concepts
|
||||
|
||||
All plugins should be subclasses of MeshPlugin. By inheriting from this class and creating an instance of your new plugin your plugin will be automatically registered to receive packets.
|
||||
|
||||
Messages are sent to particular port numbers (similar to UDP networking). Your new plugin should eventually pick its own port number (see below), but at first you can simply use PRIVATE_APP (which is the default).
|
||||
|
||||
Packets can be sent/received either as raw binary structures or as [Protobufs](https://developers.google.com/protocol-buffers).
|
||||
|
||||
### Class heirarchy
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
If your plugin needs to perform any operations at startup you can override and implement the setup() method to run your code.
|
||||
|
||||
If you need to send a packet you can call service.sendToMesh with code like this (from the examples):
|
||||
|
||||
```
|
||||
MeshPacket *p = allocReply();
|
||||
p->to = dest;
|
||||
|
||||
service.sendToMesh(p);
|
||||
```
|
||||
|
||||
## Example plugins
|
||||
|
||||
A number of [key services](/src/plugins) are implemented using the plugin API, these plugins are as follows:
|
||||
|
||||
* [TextMessagePlugin](/src/plugins/TextMessagePlugin.h) - receives text messages and displays them on the LCD screen/stores them in the local DB
|
||||
* [NodeInfoPlugin](/src/plugins/NodeInfoPlugin.h) - receives/sends User information to other nodes so that usernames are available in the databases
|
||||
* [RemoteHardwarePlugin](/src/plugins/RemoteHardwarePlugin.h) - a plugin that provides easy remote access to device hardware (for things like turning GPIOs on or off). Intended to be a more extensive example and provide a useful feature of its own. See [remote-hardware](remote-hardware.md) for details.
|
||||
* [ReplyPlugin](/src/plugins/ReplyPlugin.h) - a simple plugin that just replies to any packet it receives (provides a 'ping' service).
|
||||
|
||||
## Getting started
|
||||
|
||||
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.
|
||||
* 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"
|
||||
|
||||
## 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.
|
||||
|
||||
## 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)
|
||||
|
||||
All other values are reserved.
|
||||
|
||||
## How to add custom protocol buffers
|
||||
|
||||
If you would like to use protocol buffers to define the structures you send over the mesh (recommended), here's how to do that.
|
||||
|
||||
* 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.
|
||||
23
docs/software/remote-hardware-service.md
Normal file
23
docs/software/remote-hardware-service.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Remote Hardware Service
|
||||
|
||||
FIXME - the following are a collection of notes moved from elsewhere. We need to refactor these notes into actual documentation on the remote-hardware/gpio service.
|
||||
|
||||
### 1.7.2. New 'no-code-IOT' mini-app
|
||||
|
||||
Add a new 'remote GPIO/serial port/SPI/I2C access' mini-app. This new standard app would use the MQTT messaging layer to let users (developers that don't need to write device code) do basic (potentially dangerous) operations remotely.
|
||||
|
||||
#### 1.7.2.1. Supported operations in the initial release
|
||||
|
||||
Initially supported features for no-code-IOT.
|
||||
|
||||
- Set any GPIO
|
||||
- Read any GPIO
|
||||
|
||||
#### 1.7.2.2. Supported operations eventually
|
||||
|
||||
General ideas for no-code IOT.
|
||||
|
||||
- Subscribe for notification of GPIO input status change (i.e. when pin goes low, send my app a message)
|
||||
- Write/read N bytes over I2C/SPI bus Y (as one atomic I2C/SPI transaction)
|
||||
- Send N bytes out serial port Z
|
||||
- Subscribe for notification for when regex X matches the bytes that were received on serial port Z
|
||||
@@ -1,9 +1,10 @@
|
||||
This is a mini design doc for developing the meshtastic software.
|
||||
|
||||
* [Build instructions](build-instructions.md)
|
||||
* [On device plugin API](plugin-api.md) - a tutorial on how to write small Plugins which run on the device and can message other nodes.
|
||||
* [TODO](TODO.md) - read this if you are looking for things to do (or curious about currently missing features)
|
||||
* Our [project board](https://github.com/orgs/meshtastic/projects/1) - shows what things we are currently working on and remaining work items for the current release.
|
||||
* [Power Management](power.md)
|
||||
* [Mesh algorithm](mesh-alg.md)
|
||||
* [Device API](device-api.md) and porting guide for new clients (iOS, python, etc...)
|
||||
* [External client API](device-api.md) and porting guide for new clients (iOS, python, etc...)
|
||||
* TODO: how to port the device code to a new device.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*****************************************************************/
|
||||
|
||||
/* Enable support for dynamically allocated fields */
|
||||
#define PB_ENABLE_MALLOC 1
|
||||
/* #define PB_ENABLE_MALLOC 1 */
|
||||
|
||||
/* Define this if your CPU / compiler combination does not support
|
||||
* unaligned memory access to packed structures. */
|
||||
@@ -55,7 +55,7 @@
|
||||
|
||||
/* Version of the nanopb library. Just in case you want to check it in
|
||||
* your own program. */
|
||||
#define NANOPB_VERSION nanopb-0.4.1
|
||||
#define NANOPB_VERSION nanopb-0.4.4
|
||||
|
||||
/* Include all the system headers needed by nanopb. You will need the
|
||||
* definitions of the following:
|
||||
@@ -276,17 +276,18 @@ typedef struct pb_field_iter_s pb_field_iter_t;
|
||||
/* This structure is used in auto-generated constants
|
||||
* to specify struct fields.
|
||||
*/
|
||||
PB_PACKED_STRUCT_START
|
||||
typedef struct pb_msgdesc_s pb_msgdesc_t;
|
||||
struct pb_msgdesc_s {
|
||||
pb_size_t field_count;
|
||||
const uint32_t *field_info;
|
||||
const pb_msgdesc_t * const * submsg_info;
|
||||
const pb_byte_t *default_value;
|
||||
|
||||
bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field);
|
||||
} pb_packed;
|
||||
PB_PACKED_STRUCT_END
|
||||
|
||||
pb_size_t field_count;
|
||||
pb_size_t required_field_count;
|
||||
pb_size_t largest_tag;
|
||||
};
|
||||
|
||||
/* Iterator for message descriptor */
|
||||
struct pb_field_iter_s {
|
||||
@@ -469,137 +470,181 @@ struct pb_extension_s {
|
||||
}; \
|
||||
const pb_msgdesc_t structname ## _msg = \
|
||||
{ \
|
||||
0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \
|
||||
structname ## _field_info, \
|
||||
structname ## _submsg_info, \
|
||||
msgname ## _DEFAULT, \
|
||||
msgname ## _CALLBACK, \
|
||||
0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \
|
||||
0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \
|
||||
0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \
|
||||
}; \
|
||||
msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname)
|
||||
|
||||
#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1
|
||||
#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \
|
||||
+ (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED)
|
||||
#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \
|
||||
* 0 + tag
|
||||
|
||||
/* X-macro for generating the entries in struct_field_info[] array. */
|
||||
#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO(1, structname, atype, htype, ltype, fieldname, tag)
|
||||
PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO(2, structname, atype, htype, ltype, fieldname, tag)
|
||||
PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO(4, structname, atype, htype, ltype, fieldname, tag)
|
||||
PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO(8, structname, atype, htype, ltype, fieldname, tag)
|
||||
PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype), structname, atype, htype, ltype, fieldname, tag)
|
||||
PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \
|
||||
tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_AUTO2(width, structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO(width, structname, atype, htype, ltype, fieldname, tag)
|
||||
#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size)
|
||||
|
||||
#define PB_GEN_FIELD_INFO(width, structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_ ## width(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(htype, structname, fieldname))
|
||||
#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size)
|
||||
|
||||
/* X-macro for generating asserts that entries fit in struct_field_info[] array.
|
||||
* The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(),
|
||||
* but it is not easily reused because of how macro substitutions work. */
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO_ASSERT(1, structname, atype, htype, ltype, fieldname, tag)
|
||||
PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO_ASSERT(2, structname, atype, htype, ltype, fieldname, tag)
|
||||
PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO_ASSERT(4, structname, atype, htype, ltype, fieldname, tag)
|
||||
PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO_ASSERT(8, structname, atype, htype, ltype, fieldname, tag)
|
||||
PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype), structname, atype, htype, ltype, fieldname, tag)
|
||||
PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \
|
||||
tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname))
|
||||
|
||||
#define PB_GEN_FIELD_INFO_ASSERT_AUTO2(width, structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_GEN_FIELD_INFO_ASSERT(width, structname, atype, htype, ltype, fieldname, tag)
|
||||
#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size)
|
||||
|
||||
#define PB_GEN_FIELD_INFO_ASSERT(width, structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_FIELDINFO_ASSERT_ ## width(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \
|
||||
PB_DATA_OFFSET_ ## atype(htype, structname, fieldname), \
|
||||
PB_DATA_SIZE_ ## atype(htype, structname, fieldname), \
|
||||
PB_SIZE_OFFSET_ ## atype(htype, structname, fieldname), \
|
||||
PB_ARRAY_SIZE_ ## atype(htype, structname, fieldname))
|
||||
#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \
|
||||
PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size)
|
||||
|
||||
#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DATA_OFFSET_ ## htype(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DATA_OFFSET_ ## htype(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DATA_OFFSET_ ## htype(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_REQUIRED(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_SINGULAR(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname))
|
||||
#define PB_DATA_OFFSET_OPTIONAL(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_REPEATED(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_FIXARRAY(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
|
||||
#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname)
|
||||
#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname))
|
||||
#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname)
|
||||
#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname)
|
||||
|
||||
#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SIZE_OFFSET_ ## htype(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SIZE_OFFSET_PTR_ ## htype(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SIZE_OFFSET_CB_ ## htype(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_REQUIRED(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_SINGULAR(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_ONEOF(structname, fieldname) PB_SIZE_OFFSET_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname))
|
||||
#define PB_SIZE_OFFSET_ONEOF2(structname, fullname, unionname) PB_SIZE_OFFSET_ONEOF3(structname, fullname, unionname)
|
||||
#define PB_SIZE_OFFSET_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname)
|
||||
#define PB_SIZE_OFFSET_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname)
|
||||
#define PB_SIZE_OFFSET_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count)
|
||||
#define PB_SIZE_OFFSET_FIXARRAY(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_PTR_REQUIRED(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_PTR_SINGULAR(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_PTR_ONEOF(structname, fieldname) PB_SIZE_OFFSET_ONEOF(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_PTR_OPTIONAL(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_PTR_REPEATED(structname, fieldname) PB_SIZE_OFFSET_REPEATED(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_PTR_FIXARRAY(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_CB_REQUIRED(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_CB_SINGULAR(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_CB_ONEOF(structname, fieldname) PB_SIZE_OFFSET_ONEOF(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_CB_OPTIONAL(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_CB_REPEATED(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_CB_FIXARRAY(structname, fieldname) 0
|
||||
#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname)
|
||||
#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname)
|
||||
#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0
|
||||
#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0
|
||||
#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname))
|
||||
#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname)
|
||||
#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname)
|
||||
#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname)
|
||||
#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count)
|
||||
#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0
|
||||
#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0
|
||||
#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0
|
||||
#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname)
|
||||
#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0
|
||||
#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname)
|
||||
#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0
|
||||
#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0
|
||||
#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0
|
||||
#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname)
|
||||
#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0
|
||||
#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0
|
||||
#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0
|
||||
|
||||
#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_ARRAY_SIZE_ ## htype(structname, fieldname)
|
||||
#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_ARRAY_SIZE_PTR_ ## htype(structname, fieldname)
|
||||
#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname)
|
||||
#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname)
|
||||
#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1
|
||||
#define PB_ARRAY_SIZE_REQUIRED(structname, fieldname) 1
|
||||
#define PB_ARRAY_SIZE_SINGULAR(structname, fieldname) 1
|
||||
#define PB_ARRAY_SIZE_OPTIONAL(structname, fieldname) 1
|
||||
#define PB_ARRAY_SIZE_ONEOF(structname, fieldname) 1
|
||||
#define PB_ARRAY_SIZE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname)
|
||||
#define PB_ARRAY_SIZE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname)
|
||||
#define PB_ARRAY_SIZE_PTR_REQUIRED(structname, fieldname) 1
|
||||
#define PB_ARRAY_SIZE_PTR_SINGULAR(structname, fieldname) 1
|
||||
#define PB_ARRAY_SIZE_PTR_OPTIONAL(structname, fieldname) 1
|
||||
#define PB_ARRAY_SIZE_PTR_ONEOF(structname, fieldname) 1
|
||||
#define PB_ARRAY_SIZE_PTR_REPEATED(structname, fieldname) 1
|
||||
#define PB_ARRAY_SIZE_PTR_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0])
|
||||
#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1
|
||||
#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1
|
||||
#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1
|
||||
#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1
|
||||
#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname)
|
||||
#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname)
|
||||
#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1
|
||||
#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1
|
||||
#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1
|
||||
#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1
|
||||
#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1
|
||||
#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0])
|
||||
|
||||
#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DATA_SIZE_ ## htype(structname, fieldname)
|
||||
#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DATA_SIZE_PTR_ ## htype(structname, fieldname)
|
||||
#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DATA_SIZE_CB_ ## htype(structname, fieldname)
|
||||
#define PB_DATA_SIZE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DATA_SIZE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DATA_SIZE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DATA_SIZE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname))
|
||||
#define PB_DATA_SIZE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DATA_SIZE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DATA_SIZE_PTR_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DATA_SIZE_PTR_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DATA_SIZE_PTR_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DATA_SIZE_PTR_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0])
|
||||
#define PB_DATA_SIZE_PTR_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DATA_SIZE_PTR_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0])
|
||||
#define PB_DATA_SIZE_CB_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DATA_SIZE_CB_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DATA_SIZE_CB_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DATA_SIZE_CB_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname))
|
||||
#define PB_DATA_SIZE_CB_REPEATED(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DATA_SIZE_CB_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname)
|
||||
#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname)
|
||||
#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname)
|
||||
#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname))
|
||||
#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0])
|
||||
#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0])
|
||||
#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0])
|
||||
#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname))
|
||||
#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname)
|
||||
|
||||
#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple)
|
||||
#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname
|
||||
@@ -607,37 +652,37 @@ struct pb_extension_s {
|
||||
#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname
|
||||
|
||||
#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \
|
||||
PB_SUBMSG_INFO_ ## htype(ltype, structname, fieldname)
|
||||
PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname)
|
||||
|
||||
#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SUBMSG_INFO_ ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||
#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SUBMSG_INFO_ ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||
#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SUBMSG_INFO_ ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||
#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||
#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||
#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||
#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname))
|
||||
#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername)
|
||||
#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE)
|
||||
#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SUBMSG_INFO_ ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||
#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SUBMSG_INFO_ ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||
#define PB_SUBMSG_INFO_BOOL(t)
|
||||
#define PB_SUBMSG_INFO_BYTES(t)
|
||||
#define PB_SUBMSG_INFO_DOUBLE(t)
|
||||
#define PB_SUBMSG_INFO_ENUM(t)
|
||||
#define PB_SUBMSG_INFO_UENUM(t)
|
||||
#define PB_SUBMSG_INFO_FIXED32(t)
|
||||
#define PB_SUBMSG_INFO_FIXED64(t)
|
||||
#define PB_SUBMSG_INFO_FLOAT(t)
|
||||
#define PB_SUBMSG_INFO_INT32(t)
|
||||
#define PB_SUBMSG_INFO_INT64(t)
|
||||
#define PB_SUBMSG_INFO_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t)
|
||||
#define PB_SUBMSG_INFO_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t)
|
||||
#define PB_SUBMSG_INFO_SFIXED32(t)
|
||||
#define PB_SUBMSG_INFO_SFIXED64(t)
|
||||
#define PB_SUBMSG_INFO_SINT32(t)
|
||||
#define PB_SUBMSG_INFO_SINT64(t)
|
||||
#define PB_SUBMSG_INFO_STRING(t)
|
||||
#define PB_SUBMSG_INFO_UINT32(t)
|
||||
#define PB_SUBMSG_INFO_UINT64(t)
|
||||
#define PB_SUBMSG_INFO_EXTENSION(t)
|
||||
#define PB_SUBMSG_INFO_FIXED_LENGTH_BYTES(t)
|
||||
#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE)
|
||||
#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||
#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE)
|
||||
#define PB_SI_PB_LTYPE_BOOL(t)
|
||||
#define PB_SI_PB_LTYPE_BYTES(t)
|
||||
#define PB_SI_PB_LTYPE_DOUBLE(t)
|
||||
#define PB_SI_PB_LTYPE_ENUM(t)
|
||||
#define PB_SI_PB_LTYPE_UENUM(t)
|
||||
#define PB_SI_PB_LTYPE_FIXED32(t)
|
||||
#define PB_SI_PB_LTYPE_FIXED64(t)
|
||||
#define PB_SI_PB_LTYPE_FLOAT(t)
|
||||
#define PB_SI_PB_LTYPE_INT32(t)
|
||||
#define PB_SI_PB_LTYPE_INT64(t)
|
||||
#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t)
|
||||
#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t)
|
||||
#define PB_SI_PB_LTYPE_SFIXED32(t)
|
||||
#define PB_SI_PB_LTYPE_SFIXED64(t)
|
||||
#define PB_SI_PB_LTYPE_SINT32(t)
|
||||
#define PB_SI_PB_LTYPE_SINT64(t)
|
||||
#define PB_SI_PB_LTYPE_STRING(t)
|
||||
#define PB_SI_PB_LTYPE_UINT32(t)
|
||||
#define PB_SI_PB_LTYPE_UINT64(t)
|
||||
#define PB_SI_PB_LTYPE_EXTENSION(t)
|
||||
#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t)
|
||||
#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg),
|
||||
|
||||
/* The field descriptors use a variable width format, with width of either
|
||||
@@ -726,37 +771,37 @@ struct pb_extension_s {
|
||||
* The generator will give explicit size argument when it knows that a message
|
||||
* structure grows beyond 1-word format limits.
|
||||
*/
|
||||
#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FIELDINFO_WIDTH_ ## atype(htype, ltype)
|
||||
#define PB_FIELDINFO_WIDTH_STATIC(htype, ltype) PB_FIELDINFO_WIDTH_ ## htype(ltype)
|
||||
#define PB_FIELDINFO_WIDTH_POINTER(htype, ltype) PB_FIELDINFO_WIDTH_ ## htype(ltype)
|
||||
#define PB_FIELDINFO_WIDTH_CALLBACK(htype, ltype) 2
|
||||
#define PB_FIELDINFO_WIDTH_REQUIRED(ltype) PB_FIELDINFO_WIDTH_ ## ltype
|
||||
#define PB_FIELDINFO_WIDTH_SINGULAR(ltype) PB_FIELDINFO_WIDTH_ ## ltype
|
||||
#define PB_FIELDINFO_WIDTH_OPTIONAL(ltype) PB_FIELDINFO_WIDTH_ ## ltype
|
||||
#define PB_FIELDINFO_WIDTH_ONEOF(ltype) PB_FIELDINFO_WIDTH_ ## ltype
|
||||
#define PB_FIELDINFO_WIDTH_REPEATED(ltype) 2
|
||||
#define PB_FIELDINFO_WIDTH_FIXARRAY(ltype) 2
|
||||
#define PB_FIELDINFO_WIDTH_BOOL 1
|
||||
#define PB_FIELDINFO_WIDTH_BYTES 2
|
||||
#define PB_FIELDINFO_WIDTH_DOUBLE 1
|
||||
#define PB_FIELDINFO_WIDTH_ENUM 1
|
||||
#define PB_FIELDINFO_WIDTH_UENUM 1
|
||||
#define PB_FIELDINFO_WIDTH_FIXED32 1
|
||||
#define PB_FIELDINFO_WIDTH_FIXED64 1
|
||||
#define PB_FIELDINFO_WIDTH_FLOAT 1
|
||||
#define PB_FIELDINFO_WIDTH_INT32 1
|
||||
#define PB_FIELDINFO_WIDTH_INT64 1
|
||||
#define PB_FIELDINFO_WIDTH_MESSAGE 2
|
||||
#define PB_FIELDINFO_WIDTH_MSG_W_CB 2
|
||||
#define PB_FIELDINFO_WIDTH_SFIXED32 1
|
||||
#define PB_FIELDINFO_WIDTH_SFIXED64 1
|
||||
#define PB_FIELDINFO_WIDTH_SINT32 1
|
||||
#define PB_FIELDINFO_WIDTH_SINT64 1
|
||||
#define PB_FIELDINFO_WIDTH_STRING 2
|
||||
#define PB_FIELDINFO_WIDTH_UINT32 1
|
||||
#define PB_FIELDINFO_WIDTH_UINT64 1
|
||||
#define PB_FIELDINFO_WIDTH_EXTENSION 1
|
||||
#define PB_FIELDINFO_WIDTH_FIXED_LENGTH_BYTES 2
|
||||
#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype)
|
||||
#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype)
|
||||
#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype)
|
||||
#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2
|
||||
#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype
|
||||
#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2
|
||||
#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_BOOL 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_BYTES 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_ENUM 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_UENUM 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_INT32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_INT64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SINT32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_SINT64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_STRING 2
|
||||
#define PB_FI_WIDTH_PB_LTYPE_UINT32 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_UINT64 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1
|
||||
#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2
|
||||
|
||||
/* The mapping from protobuf types to LTYPEs is done using these macros. */
|
||||
#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL
|
||||
|
||||
@@ -32,6 +32,10 @@ bool pb_field_iter_next(pb_field_iter_t *iter);
|
||||
* Returns false if no such field exists. */
|
||||
bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag);
|
||||
|
||||
/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found.
|
||||
* There can be only one extension range field per message. */
|
||||
bool pb_field_iter_find_extension(pb_field_iter_t *iter);
|
||||
|
||||
#ifdef PB_VALIDATE_UTF8
|
||||
/* Validate UTF-8 text string */
|
||||
bool pb_validate_utf8(const char *s);
|
||||
|
||||
@@ -113,6 +113,9 @@ bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_s
|
||||
* pb_decode() returns with an error, the message is already released.
|
||||
*/
|
||||
void pb_release(const pb_msgdesc_t *fields, void *dest_struct);
|
||||
#else
|
||||
/* Allocation is not supported, so release is no-op */
|
||||
#define pb_release(fields, dest_struct) PB_UNUSED(fields); PB_UNUSED(dest_struct);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -121,11 +124,14 @@ void pb_release(const pb_msgdesc_t *fields, void *dest_struct);
|
||||
**************************************/
|
||||
|
||||
/* Create an input stream for reading from a memory buffer.
|
||||
*
|
||||
* msglen should be the actual length of the message, not the full size of
|
||||
* allocated buffer.
|
||||
*
|
||||
* Alternatively, you can use a custom stream that reads directly from e.g.
|
||||
* a file or a network socket.
|
||||
*/
|
||||
pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize);
|
||||
pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen);
|
||||
|
||||
/* Function to read from a pb_istream_t. You can use this if you need to
|
||||
* read some custom header data, or to read data in field callbacks.
|
||||
|
||||
@@ -132,7 +132,7 @@ bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count);
|
||||
* structure. Call this from the callback before writing out field contents. */
|
||||
bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field);
|
||||
|
||||
/* Encode field header by manually specifing wire type. You need to use this
|
||||
/* Encode field header by manually specifying wire type. You need to use this
|
||||
* if you want to write out packed arrays from a callback field. */
|
||||
bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number);
|
||||
|
||||
|
||||
@@ -9,89 +9,102 @@ static bool load_descriptor_values(pb_field_iter_t *iter)
|
||||
{
|
||||
uint32_t word0;
|
||||
uint32_t data_offset;
|
||||
uint_least8_t format;
|
||||
int_least8_t size_offset;
|
||||
|
||||
if (iter->index >= iter->descriptor->field_count)
|
||||
return false;
|
||||
|
||||
word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
|
||||
format = word0 & 3;
|
||||
iter->tag = (pb_size_t)((word0 >> 2) & 0x3F);
|
||||
iter->type = (pb_type_t)((word0 >> 8) & 0xFF);
|
||||
|
||||
if (format == 0)
|
||||
switch(word0 & 3)
|
||||
{
|
||||
/* 1-word format */
|
||||
iter->array_size = 1;
|
||||
size_offset = (int_least8_t)((word0 >> 24) & 0x0F);
|
||||
data_offset = (word0 >> 16) & 0xFF;
|
||||
iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F);
|
||||
}
|
||||
else if (format == 1)
|
||||
{
|
||||
/* 2-word format */
|
||||
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
|
||||
case 0: {
|
||||
/* 1-word format */
|
||||
iter->array_size = 1;
|
||||
iter->tag = (pb_size_t)((word0 >> 2) & 0x3F);
|
||||
size_offset = (int_least8_t)((word0 >> 24) & 0x0F);
|
||||
data_offset = (word0 >> 16) & 0xFF;
|
||||
iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F);
|
||||
break;
|
||||
}
|
||||
|
||||
iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF);
|
||||
iter->tag = (pb_size_t)(iter->tag | ((word1 >> 28) << 6));
|
||||
size_offset = (int_least8_t)((word0 >> 28) & 0x0F);
|
||||
data_offset = word1 & 0xFFFF;
|
||||
iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF);
|
||||
}
|
||||
else if (format == 2)
|
||||
{
|
||||
/* 4-word format */
|
||||
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
|
||||
uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
|
||||
uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
|
||||
case 1: {
|
||||
/* 2-word format */
|
||||
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
|
||||
|
||||
iter->array_size = (pb_size_t)(word0 >> 16);
|
||||
iter->tag = (pb_size_t)(iter->tag | ((word1 >> 8) << 6));
|
||||
size_offset = (int_least8_t)(word1 & 0xFF);
|
||||
data_offset = word2;
|
||||
iter->data_size = (pb_size_t)word3;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 8-word format */
|
||||
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
|
||||
uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
|
||||
uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
|
||||
uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]);
|
||||
iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF);
|
||||
iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6));
|
||||
size_offset = (int_least8_t)((word0 >> 28) & 0x0F);
|
||||
data_offset = word1 & 0xFFFF;
|
||||
iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF);
|
||||
break;
|
||||
}
|
||||
|
||||
iter->array_size = (pb_size_t)word4;
|
||||
iter->tag = (pb_size_t)(iter->tag | ((word1 >> 8) << 6));
|
||||
size_offset = (int_least8_t)(word1 & 0xFF);
|
||||
data_offset = word2;
|
||||
iter->data_size = (pb_size_t)word3;
|
||||
case 2: {
|
||||
/* 4-word format */
|
||||
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
|
||||
uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
|
||||
uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
|
||||
|
||||
iter->array_size = (pb_size_t)(word0 >> 16);
|
||||
iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6));
|
||||
size_offset = (int_least8_t)(word1 & 0xFF);
|
||||
data_offset = word2;
|
||||
iter->data_size = (pb_size_t)word3;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
/* 8-word format */
|
||||
uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]);
|
||||
uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]);
|
||||
uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]);
|
||||
uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]);
|
||||
|
||||
iter->array_size = (pb_size_t)word4;
|
||||
iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6));
|
||||
size_offset = (int_least8_t)(word1 & 0xFF);
|
||||
data_offset = word2;
|
||||
iter->data_size = (pb_size_t)word3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
iter->pField = (char*)iter->message + data_offset;
|
||||
|
||||
if (size_offset)
|
||||
{
|
||||
iter->pSize = (char*)iter->pField - size_offset;
|
||||
}
|
||||
else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED &&
|
||||
(PB_ATYPE(iter->type) == PB_ATYPE_STATIC ||
|
||||
PB_ATYPE(iter->type) == PB_ATYPE_POINTER))
|
||||
{
|
||||
/* Fixed count array */
|
||||
iter->pSize = &iter->array_size;
|
||||
}
|
||||
else
|
||||
if (!iter->message)
|
||||
{
|
||||
/* Avoid doing arithmetic on null pointers, it is undefined */
|
||||
iter->pField = NULL;
|
||||
iter->pSize = NULL;
|
||||
}
|
||||
|
||||
if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL)
|
||||
{
|
||||
iter->pData = *(void**)iter->pField;
|
||||
}
|
||||
else
|
||||
{
|
||||
iter->pData = iter->pField;
|
||||
iter->pField = (char*)iter->message + data_offset;
|
||||
|
||||
if (size_offset)
|
||||
{
|
||||
iter->pSize = (char*)iter->pField - size_offset;
|
||||
}
|
||||
else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED &&
|
||||
(PB_ATYPE(iter->type) == PB_ATYPE_STATIC ||
|
||||
PB_ATYPE(iter->type) == PB_ATYPE_POINTER))
|
||||
{
|
||||
/* Fixed count array */
|
||||
iter->pSize = &iter->array_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
iter->pSize = NULL;
|
||||
}
|
||||
|
||||
if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL)
|
||||
{
|
||||
iter->pData = *(void**)iter->pField;
|
||||
}
|
||||
else
|
||||
{
|
||||
iter->pData = iter->pField;
|
||||
}
|
||||
}
|
||||
|
||||
if (PB_LTYPE_IS_SUBMSG(iter->type))
|
||||
@@ -130,17 +143,13 @@ static void advance_iterator(pb_field_iter_t *iter)
|
||||
pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF;
|
||||
pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3));
|
||||
|
||||
/* Add to fields.
|
||||
* The cast to pb_size_t is needed to avoid -Wconversion warning.
|
||||
* Because the data is is constants from generator, there is no danger of overflow.
|
||||
*/
|
||||
iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len);
|
||||
|
||||
if (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)
|
||||
{
|
||||
iter->required_field_index++;
|
||||
}
|
||||
|
||||
if (PB_LTYPE_IS_SUBMSG(prev_type))
|
||||
{
|
||||
iter->submessage_index++;
|
||||
}
|
||||
iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED));
|
||||
iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,11 +198,23 @@ bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag)
|
||||
{
|
||||
return true; /* Nothing to do, correct field already. */
|
||||
}
|
||||
else if (tag > iter->descriptor->largest_tag)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
pb_size_t start = iter->index;
|
||||
uint32_t fieldinfo;
|
||||
|
||||
if (tag < iter->tag)
|
||||
{
|
||||
/* Fields are in tag number order, so we know that tag is between
|
||||
* 0 and our start position. Setting index to end forces
|
||||
* advance_iterator() call below to restart from beginning. */
|
||||
iter->index = iter->descriptor->field_count;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
/* Advance iterator but don't load values yet */
|
||||
@@ -222,6 +243,37 @@ bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag)
|
||||
}
|
||||
}
|
||||
|
||||
bool pb_field_iter_find_extension(pb_field_iter_t *iter)
|
||||
{
|
||||
if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pb_size_t start = iter->index;
|
||||
uint32_t fieldinfo;
|
||||
|
||||
do
|
||||
{
|
||||
/* Advance iterator but don't load values yet */
|
||||
advance_iterator(iter);
|
||||
|
||||
/* Do fast check for field type */
|
||||
fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]);
|
||||
|
||||
if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION)
|
||||
{
|
||||
return load_descriptor_values(iter);
|
||||
}
|
||||
} while (iter->index != start);
|
||||
|
||||
/* Searched all the way back to start, and found nothing. */
|
||||
(void)load_descriptor_values(iter);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void *pb_const_cast(const void *p)
|
||||
{
|
||||
/* Note: this casts away const, in order to use the common field iterator
|
||||
|
||||
@@ -24,19 +24,17 @@
|
||||
static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count);
|
||||
static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof);
|
||||
static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size);
|
||||
static bool checkreturn check_wire_type(pb_wire_type_t wire_type, pb_field_iter_t *field);
|
||||
static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_field_iter_t *field);
|
||||
static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field);
|
||||
static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field);
|
||||
static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field);
|
||||
static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field);
|
||||
static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field);
|
||||
static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type);
|
||||
static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter);
|
||||
static bool checkreturn find_extension_field(pb_field_iter_t *iter);
|
||||
static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension);
|
||||
static bool pb_field_set_to_default(pb_field_iter_t *field);
|
||||
static bool pb_message_set_to_defaults(pb_field_iter_t *iter);
|
||||
static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field);
|
||||
static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field);
|
||||
static bool checkreturn pb_dec_fixed(pb_istream_t *stream, const pb_field_iter_t *field);
|
||||
static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field);
|
||||
static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field);
|
||||
static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field);
|
||||
@@ -59,6 +57,8 @@ static void pb_release_single_field(pb_field_iter_t *field);
|
||||
#define pb_uint64_t uint64_t
|
||||
#endif
|
||||
|
||||
#define PB_WT_PACKED ((pb_wire_type_t)0xFF)
|
||||
|
||||
typedef struct {
|
||||
uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32];
|
||||
} pb_fields_seen_t;
|
||||
@@ -139,7 +139,7 @@ static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf)
|
||||
return true;
|
||||
}
|
||||
|
||||
pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize)
|
||||
pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen)
|
||||
{
|
||||
pb_istream_t stream;
|
||||
/* Cast away the const from buf without a compiler error. We are
|
||||
@@ -156,7 +156,7 @@ pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t bufsize)
|
||||
#endif
|
||||
state.c_state = buf;
|
||||
stream.state = state.state;
|
||||
stream.bytes_left = bufsize;
|
||||
stream.bytes_left = msglen;
|
||||
#ifndef PB_NO_ERRMSG
|
||||
stream.errmsg = NULL;
|
||||
#endif
|
||||
@@ -205,8 +205,10 @@ static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *d
|
||||
{
|
||||
/* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */
|
||||
pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01;
|
||||
|
||||
if ((byte & 0x7F) != 0x00 && ((result >> 31) == 0 || byte != sign_extension))
|
||||
bool valid_extension = ((byte & 0x7F) == 0x00 ||
|
||||
((result >> 31) != 0 && byte == sign_extension));
|
||||
|
||||
if (bitpos >= 64 || !valid_extension)
|
||||
{
|
||||
PB_RETURN_ERROR(stream, "varint overflow");
|
||||
}
|
||||
@@ -388,61 +390,70 @@ bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *s
|
||||
* Decode a single field *
|
||||
*************************/
|
||||
|
||||
static bool checkreturn check_wire_type(pb_wire_type_t wire_type, pb_field_iter_t *field)
|
||||
static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field)
|
||||
{
|
||||
switch (PB_LTYPE(field->type))
|
||||
{
|
||||
case PB_LTYPE_BOOL:
|
||||
case PB_LTYPE_VARINT:
|
||||
case PB_LTYPE_UVARINT:
|
||||
case PB_LTYPE_SVARINT:
|
||||
return wire_type == PB_WT_VARINT;
|
||||
if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED)
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
case PB_LTYPE_FIXED32:
|
||||
return wire_type == PB_WT_32BIT;
|
||||
|
||||
case PB_LTYPE_FIXED64:
|
||||
return wire_type == PB_WT_64BIT;
|
||||
|
||||
case PB_LTYPE_BYTES:
|
||||
case PB_LTYPE_STRING:
|
||||
case PB_LTYPE_SUBMESSAGE:
|
||||
case PB_LTYPE_SUBMSG_W_CB:
|
||||
case PB_LTYPE_FIXED_LENGTH_BYTES:
|
||||
return wire_type == PB_WT_STRING;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_field_iter_t *field)
|
||||
{
|
||||
switch (PB_LTYPE(field->type))
|
||||
{
|
||||
case PB_LTYPE_BOOL:
|
||||
return pb_dec_bool(stream, field);
|
||||
|
||||
case PB_LTYPE_VARINT:
|
||||
case PB_LTYPE_UVARINT:
|
||||
case PB_LTYPE_SVARINT:
|
||||
if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED)
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
return pb_dec_varint(stream, field);
|
||||
|
||||
case PB_LTYPE_FIXED32:
|
||||
if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED)
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
return pb_decode_fixed32(stream, field->pData);
|
||||
|
||||
case PB_LTYPE_FIXED64:
|
||||
return pb_dec_fixed(stream, field);
|
||||
if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED)
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
#ifdef PB_CONVERT_DOUBLE_FLOAT
|
||||
if (field->data_size == sizeof(float))
|
||||
{
|
||||
return pb_decode_double_as_float(stream, (float*)field->pData);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PB_WITHOUT_64BIT
|
||||
PB_RETURN_ERROR(stream, "invalid data_size");
|
||||
#else
|
||||
return pb_decode_fixed64(stream, field->pData);
|
||||
#endif
|
||||
|
||||
case PB_LTYPE_BYTES:
|
||||
if (wire_type != PB_WT_STRING)
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
return pb_dec_bytes(stream, field);
|
||||
|
||||
case PB_LTYPE_STRING:
|
||||
if (wire_type != PB_WT_STRING)
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
return pb_dec_string(stream, field);
|
||||
|
||||
case PB_LTYPE_SUBMESSAGE:
|
||||
case PB_LTYPE_SUBMSG_W_CB:
|
||||
if (wire_type != PB_WT_STRING)
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
return pb_dec_submessage(stream, field);
|
||||
|
||||
case PB_LTYPE_FIXED_LENGTH_BYTES:
|
||||
if (wire_type != PB_WT_STRING)
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
return pb_dec_fixed_length_bytes(stream, field);
|
||||
|
||||
default:
|
||||
@@ -455,18 +466,12 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t
|
||||
switch (PB_HTYPE(field->type))
|
||||
{
|
||||
case PB_HTYPE_REQUIRED:
|
||||
if (!check_wire_type(wire_type, field))
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
return decode_basic_field(stream, field);
|
||||
return decode_basic_field(stream, wire_type, field);
|
||||
|
||||
case PB_HTYPE_OPTIONAL:
|
||||
if (!check_wire_type(wire_type, field))
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
if (field->pSize != NULL)
|
||||
*(bool*)field->pSize = true;
|
||||
return decode_basic_field(stream, field);
|
||||
return decode_basic_field(stream, wire_type, field);
|
||||
|
||||
case PB_HTYPE_REPEATED:
|
||||
if (wire_type == PB_WT_STRING
|
||||
@@ -483,7 +488,7 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t
|
||||
|
||||
while (substream.bytes_left > 0 && *size < field->array_size)
|
||||
{
|
||||
if (!decode_basic_field(&substream, field))
|
||||
if (!decode_basic_field(&substream, PB_WT_PACKED, field))
|
||||
{
|
||||
status = false;
|
||||
break;
|
||||
@@ -505,18 +510,15 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t
|
||||
pb_size_t *size = (pb_size_t*)field->pSize;
|
||||
field->pData = (char*)field->pField + field->data_size * (*size);
|
||||
|
||||
if (!check_wire_type(wire_type, field))
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
if ((*size)++ >= field->array_size)
|
||||
PB_RETURN_ERROR(stream, "array overflow");
|
||||
|
||||
return decode_basic_field(stream, field);
|
||||
return decode_basic_field(stream, wire_type, field);
|
||||
}
|
||||
|
||||
case PB_HTYPE_ONEOF:
|
||||
*(pb_size_t*)field->pSize = field->tag;
|
||||
if (PB_LTYPE_IS_SUBMSG(field->type))
|
||||
if (PB_LTYPE_IS_SUBMSG(field->type) &&
|
||||
*(pb_size_t*)field->pSize != field->tag)
|
||||
{
|
||||
/* We memset to zero so that any callbacks are set to NULL.
|
||||
* This is because the callbacks might otherwise have values
|
||||
@@ -526,12 +528,14 @@ static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t
|
||||
* that can set the fields before submessage is decoded.
|
||||
* pb_dec_submessage() will set any default values. */
|
||||
memset(field->pData, 0, (size_t)field->data_size);
|
||||
|
||||
/* Set default values for the submessage fields. */
|
||||
if (!pb_field_set_to_default(field))
|
||||
PB_RETURN_ERROR(stream, "failed to set defaults");
|
||||
}
|
||||
*(pb_size_t*)field->pSize = field->tag;
|
||||
|
||||
if (!check_wire_type(wire_type, field))
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
return decode_basic_field(stream, field);
|
||||
return decode_basic_field(stream, wire_type, field);
|
||||
|
||||
default:
|
||||
PB_RETURN_ERROR(stream, "invalid field type");
|
||||
@@ -599,14 +603,8 @@ static void initialize_pointer_field(void *pItem, pb_field_iter_t *field)
|
||||
else if (PB_LTYPE_IS_SUBMSG(field->type))
|
||||
{
|
||||
/* We memset to zero so that any callbacks are set to NULL.
|
||||
* Then set any default values. */
|
||||
pb_field_iter_t submsg_iter;
|
||||
* Default values will be set by pb_dec_submessage(). */
|
||||
memset(pItem, 0, field->data_size);
|
||||
|
||||
if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, pItem))
|
||||
{
|
||||
(void)pb_message_set_to_defaults(&submsg_iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -623,9 +621,6 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_
|
||||
case PB_HTYPE_REQUIRED:
|
||||
case PB_HTYPE_OPTIONAL:
|
||||
case PB_HTYPE_ONEOF:
|
||||
if (!check_wire_type(wire_type, field))
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL)
|
||||
{
|
||||
/* Duplicate field, have to release the old allocation first. */
|
||||
@@ -643,7 +638,7 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_
|
||||
{
|
||||
/* pb_dec_string and pb_dec_bytes handle allocation themselves */
|
||||
field->pData = field->pField;
|
||||
return decode_basic_field(stream, field);
|
||||
return decode_basic_field(stream, wire_type, field);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -652,7 +647,7 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_
|
||||
|
||||
field->pData = *(void**)field->pField;
|
||||
initialize_pointer_field(field->pData, field);
|
||||
return decode_basic_field(stream, field);
|
||||
return decode_basic_field(stream, wire_type, field);
|
||||
}
|
||||
|
||||
case PB_HTYPE_REPEATED:
|
||||
@@ -700,7 +695,7 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_
|
||||
/* Decode the array entry */
|
||||
field->pData = *(char**)field->pField + field->data_size * (*size);
|
||||
initialize_pointer_field(field->pData, field);
|
||||
if (!decode_basic_field(&substream, field))
|
||||
if (!decode_basic_field(&substream, PB_WT_PACKED, field))
|
||||
{
|
||||
status = false;
|
||||
break;
|
||||
@@ -721,16 +716,13 @@ static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_
|
||||
if (*size == PB_SIZE_MAX)
|
||||
PB_RETURN_ERROR(stream, "too many array entries");
|
||||
|
||||
if (!check_wire_type(wire_type, field))
|
||||
PB_RETURN_ERROR(stream, "wrong wire type");
|
||||
|
||||
if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1)))
|
||||
return false;
|
||||
|
||||
field->pData = *(char**)field->pField + field->data_size * (*size);
|
||||
(*size)++;
|
||||
initialize_pointer_field(field->pData, field);
|
||||
return decode_basic_field(stream, field);
|
||||
return decode_basic_field(stream, wire_type, field);
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -821,7 +813,7 @@ static bool checkreturn default_extension_decoder(pb_istream_t *stream,
|
||||
if (!pb_field_iter_begin_extension(&iter, extension))
|
||||
PB_RETURN_ERROR(stream, "invalid extension");
|
||||
|
||||
if (iter.tag != tag)
|
||||
if (iter.tag != tag || !iter.message)
|
||||
return true;
|
||||
|
||||
extension->found = true;
|
||||
@@ -831,9 +823,8 @@ static bool checkreturn default_extension_decoder(pb_istream_t *stream,
|
||||
/* Try to decode an unknown field as an extension field. Tries each extension
|
||||
* decoder in turn, until one of them handles the field or loop ends. */
|
||||
static bool checkreturn decode_extension(pb_istream_t *stream,
|
||||
uint32_t tag, pb_wire_type_t wire_type, pb_field_iter_t *iter)
|
||||
uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension)
|
||||
{
|
||||
pb_extension_t *extension = *(pb_extension_t* const *)iter->pData;
|
||||
size_t pos = stream->bytes_left;
|
||||
|
||||
while (extension != NULL && pos == stream->bytes_left)
|
||||
@@ -853,22 +844,6 @@ static bool checkreturn decode_extension(pb_istream_t *stream,
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Step through the iterator until an extension field is found or until all
|
||||
* entries have been checked. There can be only one extension field per
|
||||
* message. Returns false if no extension field is found. */
|
||||
static bool checkreturn find_extension_field(pb_field_iter_t *iter)
|
||||
{
|
||||
pb_size_t start = iter->index;
|
||||
|
||||
do {
|
||||
if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION)
|
||||
return true;
|
||||
(void)pb_field_iter_next(iter);
|
||||
} while (iter->index != start);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Initialize message fields to default values, recursively */
|
||||
static bool pb_field_set_to_default(pb_field_iter_t *field)
|
||||
{
|
||||
@@ -910,9 +885,14 @@ static bool pb_field_set_to_default(pb_field_iter_t *field)
|
||||
|
||||
if (init_data)
|
||||
{
|
||||
if (PB_LTYPE_IS_SUBMSG(field->type))
|
||||
if (PB_LTYPE_IS_SUBMSG(field->type) &&
|
||||
(field->submsg_desc->default_value != NULL ||
|
||||
field->submsg_desc->field_callback != NULL ||
|
||||
field->submsg_desc->submsg_info[0] != NULL))
|
||||
{
|
||||
/* Initialize submessage to defaults */
|
||||
/* Initialize submessage to defaults.
|
||||
* Only needed if it has default values
|
||||
* or callback/submessage fields. */
|
||||
pb_field_iter_t submsg_iter;
|
||||
if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData))
|
||||
{
|
||||
@@ -989,6 +969,7 @@ static bool pb_message_set_to_defaults(pb_field_iter_t *iter)
|
||||
static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags)
|
||||
{
|
||||
uint32_t extension_range_start = 0;
|
||||
pb_extension_t *extensions = NULL;
|
||||
|
||||
/* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed
|
||||
* count field. This can only handle _one_ repeated fixed count field that
|
||||
@@ -1040,25 +1021,31 @@ static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t
|
||||
if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION)
|
||||
{
|
||||
/* No match found, check if it matches an extension. */
|
||||
if (extension_range_start == 0)
|
||||
{
|
||||
if (pb_field_iter_find_extension(&iter))
|
||||
{
|
||||
extensions = *(pb_extension_t* const *)iter.pData;
|
||||
extension_range_start = iter.tag;
|
||||
}
|
||||
|
||||
if (!extensions)
|
||||
{
|
||||
extension_range_start = (uint32_t)-1;
|
||||
}
|
||||
}
|
||||
|
||||
if (tag >= extension_range_start)
|
||||
{
|
||||
if (!find_extension_field(&iter))
|
||||
extension_range_start = (uint32_t)-1;
|
||||
else
|
||||
extension_range_start = iter.tag;
|
||||
size_t pos = stream->bytes_left;
|
||||
|
||||
if (tag >= extension_range_start)
|
||||
if (!decode_extension(stream, tag, wire_type, extensions))
|
||||
return false;
|
||||
|
||||
if (pos != stream->bytes_left)
|
||||
{
|
||||
size_t pos = stream->bytes_left;
|
||||
|
||||
if (!decode_extension(stream, tag, wire_type, &iter))
|
||||
return false;
|
||||
|
||||
if (pos != stream->bytes_left)
|
||||
{
|
||||
/* The field was handled */
|
||||
continue;
|
||||
}
|
||||
/* The field was handled */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1112,27 +1099,15 @@ static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t
|
||||
|
||||
/* Check that all required fields were present. */
|
||||
{
|
||||
/* First figure out the number of required fields by
|
||||
* seeking to the end of the field array. Usually we
|
||||
* are already close to end after decoding.
|
||||
*/
|
||||
pb_size_t req_field_count;
|
||||
pb_type_t last_type;
|
||||
pb_size_t i;
|
||||
do {
|
||||
req_field_count = iter.required_field_index;
|
||||
last_type = iter.type;
|
||||
} while (pb_field_iter_next(&iter));
|
||||
|
||||
/* Fixup if last field was also required. */
|
||||
if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED && iter.tag != 0)
|
||||
req_field_count++;
|
||||
|
||||
if (req_field_count > PB_MAX_REQUIRED_FIELDS)
|
||||
req_field_count = PB_MAX_REQUIRED_FIELDS;
|
||||
pb_size_t req_field_count = iter.descriptor->required_field_count;
|
||||
|
||||
if (req_field_count > 0)
|
||||
{
|
||||
pb_size_t i;
|
||||
|
||||
if (req_field_count > PB_MAX_REQUIRED_FIELDS)
|
||||
req_field_count = PB_MAX_REQUIRED_FIELDS;
|
||||
|
||||
/* Check the whole words */
|
||||
for (i = 0; i < (req_field_count >> 5); i++)
|
||||
{
|
||||
@@ -1277,7 +1252,7 @@ static void pb_release_single_field(pb_field_iter_t *field)
|
||||
|
||||
if (field->pData)
|
||||
{
|
||||
while (count--)
|
||||
for (; count > 0; count--)
|
||||
{
|
||||
pb_release(field->submsg_desc, field->pData);
|
||||
field->pData = (char*)field->pData + field->data_size;
|
||||
@@ -1294,7 +1269,7 @@ static void pb_release_single_field(pb_field_iter_t *field)
|
||||
/* Release entries in repeated string or bytes array */
|
||||
void **pItem = *(void***)field->pField;
|
||||
pb_size_t count = *(pb_size_t*)field->pSize;
|
||||
while (count--)
|
||||
for (; count > 0; count--)
|
||||
{
|
||||
pb_free(*pItem);
|
||||
*pItem++ = NULL;
|
||||
@@ -1454,7 +1429,7 @@ static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_
|
||||
|
||||
/* See issue 97: Google's C++ protobuf allows negative varint values to
|
||||
* be cast as int32_t, instead of the int64_t that should be used when
|
||||
* encoding. Previous nanopb versions had a bug in encoding. In order to
|
||||
* encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to
|
||||
* not break decoding of such messages, we cast <=32 bit fields to
|
||||
* int32_t first to get the sign correct.
|
||||
*/
|
||||
@@ -1483,31 +1458,6 @@ static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkreturn pb_dec_fixed(pb_istream_t *stream, const pb_field_iter_t *field)
|
||||
{
|
||||
#ifdef PB_CONVERT_DOUBLE_FLOAT
|
||||
if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
|
||||
{
|
||||
return pb_decode_double_as_float(stream, (float*)field->pData);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (field->data_size == sizeof(uint32_t))
|
||||
{
|
||||
return pb_decode_fixed32(stream, field->pData);
|
||||
}
|
||||
#ifndef PB_WITHOUT_64BIT
|
||||
else if (field->data_size == sizeof(uint64_t))
|
||||
{
|
||||
return pb_decode_fixed64(stream, field->pData);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
PB_RETURN_ERROR(stream, "invalid data_size");
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field)
|
||||
{
|
||||
uint32_t size;
|
||||
@@ -1601,6 +1551,7 @@ static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_
|
||||
static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field)
|
||||
{
|
||||
bool status = true;
|
||||
bool submsg_consumed = false;
|
||||
pb_istream_t substream;
|
||||
|
||||
if (!pb_make_string_substream(stream, &substream))
|
||||
@@ -1609,19 +1560,6 @@ static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_i
|
||||
if (field->submsg_desc == NULL)
|
||||
PB_RETURN_ERROR(stream, "invalid field descriptor");
|
||||
|
||||
/* New array entries need to be initialized, while required and optional
|
||||
* submessages have already been initialized in the top-level pb_decode. */
|
||||
if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED ||
|
||||
PB_HTYPE(field->type) == PB_HTYPE_ONEOF)
|
||||
{
|
||||
pb_field_iter_t submsg_iter;
|
||||
if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData))
|
||||
{
|
||||
if (!pb_message_set_to_defaults(&submsg_iter))
|
||||
PB_RETURN_ERROR(stream, "failed to set defaults");
|
||||
}
|
||||
}
|
||||
|
||||
/* Submessages can have a separate message-level callback that is called
|
||||
* before decoding the message. Typically it is used to set callback fields
|
||||
* inside oneofs. */
|
||||
@@ -1632,13 +1570,28 @@ static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_i
|
||||
if (callback->funcs.decode)
|
||||
{
|
||||
status = callback->funcs.decode(&substream, field, &callback->arg);
|
||||
|
||||
if (substream.bytes_left == 0)
|
||||
{
|
||||
submsg_consumed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now decode the submessage contents */
|
||||
if (status)
|
||||
if (status && !submsg_consumed)
|
||||
{
|
||||
status = pb_decode_inner(&substream, field->submsg_desc, field->pData, 0);
|
||||
unsigned int flags = 0;
|
||||
|
||||
/* Static required/optional fields are already initialized by top-level
|
||||
* pb_decode(), no need to initialize them again. */
|
||||
if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
|
||||
PB_HTYPE(field->type) != PB_HTYPE_REPEATED)
|
||||
{
|
||||
flags = PB_DECODE_NOINIT;
|
||||
}
|
||||
|
||||
status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags);
|
||||
}
|
||||
|
||||
if (!pb_close_string_substream(stream, &substream))
|
||||
@@ -1692,37 +1645,41 @@ bool pb_decode_double_as_float(pb_istream_t *stream, float *dest)
|
||||
{
|
||||
/* Special value */
|
||||
exponent = 128;
|
||||
}
|
||||
else if (exponent > 127)
|
||||
{
|
||||
/* Too large, convert to infinity */
|
||||
exponent = 128;
|
||||
mantissa = 0;
|
||||
}
|
||||
else if (exponent < -150)
|
||||
{
|
||||
/* Too small, convert to zero */
|
||||
exponent = -127;
|
||||
mantissa = 0;
|
||||
}
|
||||
else if (exponent < -126)
|
||||
{
|
||||
/* Denormalized */
|
||||
mantissa |= 0x1000000;
|
||||
mantissa >>= (-126 - exponent);
|
||||
exponent = -127;
|
||||
}
|
||||
|
||||
/* Round off mantissa */
|
||||
mantissa = (mantissa + 1) >> 1;
|
||||
|
||||
/* Check if mantissa went over 2.0 */
|
||||
if (mantissa & 0x800000)
|
||||
{
|
||||
exponent += 1;
|
||||
mantissa &= 0x7FFFFF;
|
||||
mantissa >>= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exponent > 127)
|
||||
{
|
||||
/* Too large, convert to infinity */
|
||||
exponent = 128;
|
||||
mantissa = 0;
|
||||
}
|
||||
else if (exponent < -150)
|
||||
{
|
||||
/* Too small, convert to zero */
|
||||
exponent = -127;
|
||||
mantissa = 0;
|
||||
}
|
||||
else if (exponent < -126)
|
||||
{
|
||||
/* Denormalized */
|
||||
mantissa |= 0x1000000;
|
||||
mantissa >>= (-126 - exponent);
|
||||
exponent = -127;
|
||||
}
|
||||
|
||||
/* Round off mantissa */
|
||||
mantissa = (mantissa + 1) >> 1;
|
||||
|
||||
/* Check if mantissa went over 2.0 */
|
||||
if (mantissa & 0x800000)
|
||||
{
|
||||
exponent += 1;
|
||||
mantissa &= 0x7FFFFF;
|
||||
mantissa >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine fields */
|
||||
out.i = mantissa;
|
||||
|
||||
@@ -82,8 +82,11 @@ bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t cou
|
||||
{
|
||||
if (count > 0 && stream->callback != NULL)
|
||||
{
|
||||
if (stream->bytes_written + count > stream->max_size)
|
||||
if (stream->bytes_written + count < stream->bytes_written ||
|
||||
stream->bytes_written + count > stream->max_size)
|
||||
{
|
||||
PB_RETURN_ERROR(stream, "stream full");
|
||||
}
|
||||
|
||||
#ifdef PB_BUFFER_ONLY
|
||||
if (!buf_write(stream, buf, count))
|
||||
@@ -262,9 +265,33 @@ static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *fie
|
||||
* submessage fields. */
|
||||
return safe_read_bool(field->pSize) == false;
|
||||
}
|
||||
else if (field->descriptor->default_value)
|
||||
{
|
||||
/* Proto3 messages do not have default values, but proto2 messages
|
||||
* can contain optional fields without has_fields (generator option 'proto3').
|
||||
* In this case they must always be encoded, to make sure that the
|
||||
* non-zero default value is overwritten.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Rest is proto3 singular fields */
|
||||
if (PB_LTYPE(type) == PB_LTYPE_BYTES)
|
||||
if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE)
|
||||
{
|
||||
/* Simple integer / float fields */
|
||||
pb_size_t i;
|
||||
const char *p = (const char*)field->pData;
|
||||
for (i = 0; i < field->data_size; i++)
|
||||
{
|
||||
if (p[i] != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (PB_LTYPE(type) == PB_LTYPE_BYTES)
|
||||
{
|
||||
const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData;
|
||||
return bytes->size == 0;
|
||||
@@ -302,27 +329,29 @@ static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *fie
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
else if (PB_ATYPE(type) == PB_ATYPE_POINTER)
|
||||
{
|
||||
/* Catch-all branch that does byte-per-byte comparison for zero value.
|
||||
*
|
||||
* This is for all pointer fields, and for static PB_LTYPE_VARINT,
|
||||
* UVARINT, SVARINT, FIXED32, FIXED64, EXTENSION fields, and also
|
||||
* callback fields. These all have integer or pointer value which
|
||||
* can be compared with 0.
|
||||
*/
|
||||
pb_size_t i;
|
||||
const char *p = (const char*)field->pData;
|
||||
for (i = 0; i < field->data_size; i++)
|
||||
{
|
||||
if (p[i] != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return field->pData == NULL;
|
||||
}
|
||||
else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK)
|
||||
{
|
||||
if (PB_LTYPE(type) == PB_LTYPE_EXTENSION)
|
||||
{
|
||||
const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData;
|
||||
return extension == NULL;
|
||||
}
|
||||
else if (field->descriptor->field_callback == pb_default_field_callback)
|
||||
{
|
||||
pb_callback_t *pCallback = (pb_callback_t*)field->pData;
|
||||
return pCallback->funcs.encode == NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return field->descriptor->field_callback == NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return false; /* Not typically reached, safe default for weird special cases. */
|
||||
}
|
||||
|
||||
/* Encode a field with static or pointer allocation, i.e. one whose data
|
||||
@@ -823,7 +852,7 @@ static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t
|
||||
}
|
||||
|
||||
if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
|
||||
PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) > field->data_size)
|
||||
bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes))
|
||||
{
|
||||
PB_RETURN_ERROR(stream, "bytes size exceeded");
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ lib_deps =
|
||||
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||
lib_ignore = segger_rtt
|
||||
platform_packages =
|
||||
framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#2814f110aa618429bdd9a0a2d6a93c55f29f87a6
|
||||
framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#352c8ea7cb73f10433ed139f34251979c470ad56
|
||||
|
||||
; customize the partition table
|
||||
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
|
||||
|
||||
2
proto
2
proto
Submodule proto updated: ebd18145ca...dfe7bc1217
@@ -16,6 +16,10 @@ template <class T> class Observer
|
||||
public:
|
||||
virtual ~Observer();
|
||||
|
||||
/// Stop watching our current obserable
|
||||
void unobserve();
|
||||
|
||||
/// Start watching a specified observable
|
||||
void observe(Observable<T> *o);
|
||||
|
||||
private:
|
||||
@@ -82,6 +86,11 @@ template <class T> class Observable
|
||||
};
|
||||
|
||||
template <class T> Observer<T>::~Observer()
|
||||
{
|
||||
unobserve();
|
||||
}
|
||||
|
||||
template <class T> void Observer<T>::unobserve()
|
||||
{
|
||||
if (observed)
|
||||
observed->removeObserver(this);
|
||||
|
||||
@@ -268,7 +268,9 @@ 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_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 0
|
||||
|
||||
// Not connected
|
||||
|
||||
@@ -144,12 +144,12 @@ static void onEnter()
|
||||
|
||||
uint32_t now = millis();
|
||||
|
||||
if (now - lastPingMs > 30 * 1000) { // if more than a minute since our last press, ask other nodes to update their state
|
||||
if (now - lastPingMs >
|
||||
30 * 1000) { // if more than a minute since our last press, ask node we are looking at to update their state
|
||||
if (displayedNodeNum)
|
||||
service.sendNetworkPing(displayedNodeNum, true); // Refresh the currently displayed node
|
||||
lastPingMs = now;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void screenPress()
|
||||
@@ -171,11 +171,18 @@ Fsm powerFSM(&stateBOOT);
|
||||
|
||||
void PowerFSM_setup()
|
||||
{
|
||||
bool isRouter = radioConfig.preferences.is_router;
|
||||
|
||||
// If we are not a router and we already have AC power go to POWER state after init, otherwise go to ON
|
||||
// We assume routers might be powered all the time, but from a low current (solar) source
|
||||
bool isLowPower = radioConfig.preferences.is_low_power;
|
||||
bool hasPower = !isLowPower && powerStatus && powerStatus->getHasUSB();
|
||||
bool isRouter = radioConfig.preferences.is_router;
|
||||
bool isLowPower = radioConfig.preferences.is_low_power || isRouter;
|
||||
|
||||
/* To determine if we're externally powered, assumptions
|
||||
1) If we're powered up and there's no battery, we must be getting power externally.
|
||||
2) If we detect USB power from the power management chip, we must be getting power externally.
|
||||
*/
|
||||
bool hasPower = (powerStatus && !powerStatus->getHasBattery()) || (!isLowPower && powerStatus && powerStatus->getHasUSB());
|
||||
|
||||
DEBUG_MSG("PowerFSM init, USB power=%d\n", hasPower);
|
||||
powerFSM.add_timed_transition(&stateBOOT, hasPower ? &statePOWER : &stateON, 3 * 1000, NULL, "boot timeout");
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "RedirectablePrint.h"
|
||||
#include "configuration.h"
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include "concurrency/OSThread.h"
|
||||
|
||||
/**
|
||||
* A printer that doesn't go anywhere
|
||||
@@ -15,10 +18,82 @@ void RedirectablePrint::setDestination(Print *_dest)
|
||||
|
||||
size_t RedirectablePrint::write(uint8_t c)
|
||||
{
|
||||
// Always send the characters to our segger JTAG debugger
|
||||
#ifdef SEGGER_STDOUT_CH
|
||||
SEGGER_RTT_PutCharSkip(SEGGER_STDOUT_CH, c);
|
||||
#endif
|
||||
|
||||
dest->write(c);
|
||||
return 1; // We always claim one was written, rather than trusting what the serial port said (which could be zero)
|
||||
}
|
||||
|
||||
size_t RedirectablePrint::vprintf(const char *format, va_list arg)
|
||||
{
|
||||
va_list copy;
|
||||
|
||||
va_copy(copy, arg);
|
||||
int len = vsnprintf(printBuf, printBufLen, format, copy);
|
||||
va_end(copy);
|
||||
if (len < 0) {
|
||||
va_end(arg);
|
||||
return 0;
|
||||
};
|
||||
if (len >= printBufLen) {
|
||||
delete[] printBuf;
|
||||
printBufLen *= 2;
|
||||
printBuf = new char[printBufLen];
|
||||
len = vsnprintf(printBuf, printBufLen, format, arg);
|
||||
}
|
||||
|
||||
len = Print::write(printBuf, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
#define SEC_PER_DAY 86400
|
||||
#define SEC_PER_HOUR 3600
|
||||
#define SEC_PER_MIN 60
|
||||
|
||||
size_t RedirectablePrint::logDebug(const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
|
||||
// Cope with 0 len format strings, but look for new line terminator
|
||||
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
|
||||
|
||||
size_t r = 0;
|
||||
|
||||
// If we are the first message on a report, include the header
|
||||
if (!isContinuationMessage) {
|
||||
struct timeval tv;
|
||||
if (!gettimeofday(&tv, NULL)) {
|
||||
long hms = tv.tv_sec % SEC_PER_DAY;
|
||||
//hms += tz.tz_dsttime * SEC_PER_HOUR;
|
||||
//hms -= tz.tz_minuteswest * SEC_PER_MIN;
|
||||
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
|
||||
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||
|
||||
// Tear apart hms into h:m:s
|
||||
int hour = hms / SEC_PER_HOUR;
|
||||
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
||||
|
||||
r += printf("%02d:%02d:%02d ", hour, min, sec);
|
||||
} else
|
||||
r += printf("??:??:?? ");
|
||||
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
if(thread) {
|
||||
print("[");
|
||||
print(thread->ThreadName);
|
||||
print("] ");
|
||||
}
|
||||
}
|
||||
|
||||
r += vprintf(format, arg);
|
||||
va_end(arg);
|
||||
|
||||
isContinuationMessage = !hasNewline;
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <Print.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
* A Printable that can be switched to squirt its bytes to a different sink.
|
||||
@@ -11,6 +12,13 @@ class RedirectablePrint : public Print
|
||||
{
|
||||
Print *dest;
|
||||
|
||||
/// We dynamically grow this scratch buffer if necessary
|
||||
char *printBuf = new char[64];
|
||||
size_t printBufLen = 64;
|
||||
|
||||
/// Used to allow multiple logDebug messages to appear on a single log line
|
||||
bool isContinuationMessage = false;
|
||||
|
||||
public:
|
||||
RedirectablePrint(Print *_dest) : dest(_dest) {}
|
||||
|
||||
@@ -20,6 +28,21 @@ class RedirectablePrint : public Print
|
||||
void setDestination(Print *dest);
|
||||
|
||||
virtual size_t write(uint8_t c);
|
||||
|
||||
/**
|
||||
* Debug logging print message
|
||||
*
|
||||
* If the provide format string ends with a newline we assume it is the final print of a single
|
||||
* log message. Otherwise we assume more prints will come before the log message ends. This
|
||||
* allows you to call logDebug a few times to build up a single log message line if you wish.
|
||||
*
|
||||
* FIXME, eventually add log levels (INFO, WARN, ERROR) and subsystems. Move into
|
||||
* a different class.
|
||||
*/
|
||||
size_t logDebug(const char * format, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
/** like printf but va_list based */
|
||||
size_t vprintf(const char *format, va_list arg);
|
||||
};
|
||||
|
||||
class NoopPrint : public Print
|
||||
|
||||
88
src/airtime.cpp
Normal file
88
src/airtime.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "airtime.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
#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.
|
||||
//
|
||||
uint32_t secondsPerPeriod = 3600;
|
||||
uint32_t lastMillis = 0;
|
||||
uint32_t secSinceBoot = 0;
|
||||
|
||||
// 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];
|
||||
uint8_t lastPeriodIndex;
|
||||
} airtimes;
|
||||
|
||||
void logAirtime(reportTypes reportType, uint32_t airtime_ms)
|
||||
{
|
||||
|
||||
if (reportType == TX_LOG) {
|
||||
airtimes.periodTX[0] = airtimes.periodTX[0] + round(airtime_ms / 1000);
|
||||
} else if (reportType == RX_LOG) {
|
||||
airtimes.periodRX[0] = airtimes.periodRX[0] + round(airtime_ms / 1000);
|
||||
} else if (reportType == RX_ALL_LOG) {
|
||||
airtimes.periodRX_ALL[0] = airtimes.periodRX_ALL[0] + round(airtime_ms / 1000);
|
||||
} else {
|
||||
// Unknown report type
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t currentPeriodIndex()
|
||||
{
|
||||
return ((getSecondsSinceBoot() / secondsPerPeriod) % periodsToLog);
|
||||
}
|
||||
|
||||
void airtimeCalculator()
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t *airtimeReport(reportTypes reportType)
|
||||
{
|
||||
// currentHourIndexReset();
|
||||
|
||||
if (reportType == TX_LOG) {
|
||||
return airtimes.periodTX;
|
||||
} else if (reportType == RX_LOG) {
|
||||
return airtimes.periodRX;
|
||||
} else if (reportType == RX_ALL_LOG) {
|
||||
return airtimes.periodRX_ALL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t getPeriodsToLog()
|
||||
{
|
||||
return periodsToLog;
|
||||
}
|
||||
|
||||
uint32_t getSecondsPerPeriod()
|
||||
{
|
||||
return secondsPerPeriod;
|
||||
}
|
||||
|
||||
uint32_t getSecondsSinceBoot()
|
||||
{
|
||||
return secSinceBoot;
|
||||
}
|
||||
38
src/airtime.h
Normal file
38
src/airtime.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
|
||||
/*
|
||||
TX_LOG - Time on air this device has transmitted
|
||||
|
||||
RX_LOG - Time on air used by valid and routable mesh packets, does not include
|
||||
TX air time
|
||||
|
||||
RX_ALL_LOG - Time of all received lora packets. This includes packets that are not
|
||||
for meshtastic devices. Does not include TX air time.
|
||||
|
||||
Example analytics:
|
||||
|
||||
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.
|
||||
|
||||
RX_ALL_LOG - RX_LOG = Other lora radios on our frequency channel.
|
||||
*/
|
||||
enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG };
|
||||
|
||||
void logAirtime(reportTypes reportType, uint32_t airtime_ms);
|
||||
|
||||
void airtimeCalculator();
|
||||
|
||||
uint8_t currentPeriodIndex();
|
||||
uint8_t getPeriodsToLog();
|
||||
|
||||
uint32_t getSecondsSinceBoot();
|
||||
|
||||
uint16_t *airtimeReport(reportTypes reportType);
|
||||
|
||||
uint32_t getSecondsPerPeriod();
|
||||
@@ -14,6 +14,8 @@ bool OSThread::showRun = false;
|
||||
/// Show debugging info for threads we decide not to run;
|
||||
bool OSThread::showWaiting = false;
|
||||
|
||||
const OSThread *OSThread::currentThread;
|
||||
|
||||
ThreadController mainController, timerController;
|
||||
InterruptableDelay mainDelay;
|
||||
|
||||
@@ -68,12 +70,15 @@ bool OSThread::shouldRun(unsigned long time)
|
||||
|
||||
void OSThread::run()
|
||||
{
|
||||
currentThread = this;
|
||||
auto newDelay = runOnce();
|
||||
|
||||
runned();
|
||||
|
||||
if (newDelay >= 0)
|
||||
setInterval(newDelay);
|
||||
|
||||
currentThread = NULL;
|
||||
}
|
||||
|
||||
} // namespace concurrency
|
||||
|
||||
@@ -42,6 +42,10 @@ class OSThread : public Thread
|
||||
static bool showWaiting;
|
||||
|
||||
public:
|
||||
|
||||
/// For debug printing only (might be null)
|
||||
static const OSThread *currentThread;
|
||||
|
||||
OSThread(const char *name, uint32_t period = 0, ThreadController *controller = &mainController);
|
||||
|
||||
virtual ~OSThread();
|
||||
|
||||
@@ -289,6 +289,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define GPS_RX_PIN 36
|
||||
#define GPS_TX_PIN 13 // per @eugene
|
||||
|
||||
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
|
||||
|
||||
#define I2C_SDA 21 // I2C pins for this board
|
||||
#define I2C_SCL 22
|
||||
|
||||
@@ -428,7 +430,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define DEBUG_MSG(...) SEGGER_RTT_printf(0, __VA_ARGS__)
|
||||
#else
|
||||
#ifdef DEBUG_PORT
|
||||
#define DEBUG_MSG(...) DEBUG_PORT.printf(__VA_ARGS__)
|
||||
#define DEBUG_MSG(...) DEBUG_PORT.logDebug(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_MSG(...)
|
||||
#endif
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
/// Error codes for critical error
|
||||
enum CriticalErrorCode { NoError, ErrTxWatchdog, ErrSleepEnterWait, ErrNoRadio, ErrUnspecified, UBloxInitFailed };
|
||||
#include "mesh/generated/mesh.pb.h" // For CriticalErrorCode
|
||||
|
||||
/// Record an error that should be reported via analytics
|
||||
void recordCriticalError(CriticalErrorCode code, uint32_t address = 0);
|
||||
void recordCriticalError(CriticalErrorCode code = CriticalErrorCode_Unspecified, uint32_t address = 0);
|
||||
|
||||
@@ -32,9 +32,14 @@ void WiFiServerAPI::loop()
|
||||
{
|
||||
if (client.connected()) {
|
||||
StreamAPI::loop();
|
||||
} else {
|
||||
DEBUG_MSG("Client dropped connection, closing TCP server\n");
|
||||
delete this;
|
||||
} else if(isConnected) {
|
||||
// If our API link was up, shut it down
|
||||
|
||||
DEBUG_MSG("Client dropped connection, closing API client\n");
|
||||
// Note: we can't call delete here because this object includes other state
|
||||
// besides the stream API. Instead kill it later when we start a new instance
|
||||
// delete this;
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ uint32_t GPS::getWakeTime() const
|
||||
return t; // already maxint
|
||||
|
||||
if (t == 0)
|
||||
t = 15 * 60; // Allow up to 5 mins for each attempt (probably will be much less if we can find sats)
|
||||
t = radioConfig.preferences.is_router ? 5 * 60 : 15 * 60; // Allow up to 15 mins for each attempt (probably will be much less if we can find sats) or less if a router
|
||||
|
||||
t *= 1000; // msecs
|
||||
|
||||
@@ -179,8 +179,8 @@ uint32_t GPS::getSleepTime() const
|
||||
if (t == UINT32_MAX)
|
||||
return t; // already maxint
|
||||
|
||||
if (t == 0)
|
||||
t = 2 * 60; // 2 mins
|
||||
if (t == 0) // default - unset in preferences
|
||||
t = radioConfig.preferences.is_router ? 24 * 60 * 60 : 2 * 60; // 2 mins or once per day for routers
|
||||
|
||||
t *= 1000;
|
||||
|
||||
|
||||
@@ -31,9 +31,23 @@ void readFromRTC()
|
||||
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
|
||||
bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
|
||||
{
|
||||
static uint32_t lastSetMsec = 0;
|
||||
uint32_t now = millis();
|
||||
|
||||
bool shouldSet;
|
||||
if (q > currentQuality) {
|
||||
currentQuality = q;
|
||||
DEBUG_MSG("Setting RTC %ld secs\n", tv->tv_sec);
|
||||
shouldSet = true;
|
||||
DEBUG_MSG("Upgrading time to RTC %ld secs (quality %d)\n", tv->tv_sec, q);
|
||||
} else if(q == RTCQualityGPS && (now - lastSetMsec) > (12 * 60 * 60 * 1000L)) {
|
||||
// Every 12 hrs we will slam in a new GPS time, to correct for local RTC clock drift
|
||||
shouldSet = true;
|
||||
DEBUG_MSG("Reapplying GPS time to correct clock drift %ld secs\n", tv->tv_sec);
|
||||
}
|
||||
else
|
||||
shouldSet = false;
|
||||
|
||||
if (shouldSet) {
|
||||
lastSetMsec = now;
|
||||
#ifndef NO_ESP32
|
||||
settimeofday(tv, NULL);
|
||||
#else
|
||||
|
||||
@@ -43,7 +43,7 @@ bool UBloxGPS::setupGPS()
|
||||
DEBUG_MSG("Connected to UBLOX GPS successfully\n");
|
||||
|
||||
if (!setUBXMode())
|
||||
recordCriticalError(UBloxInitFailed); // Don't halt the boot if saving the config fails, but do report the bug
|
||||
recordCriticalError(CriticalErrorCode_UBloxInitFailed); // Don't halt the boot if saving the config fails, but do report the bug
|
||||
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -26,7 +26,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "Screen.h"
|
||||
#include "configs.h"
|
||||
#include "configuration.h"
|
||||
#include "graphics/images.h"
|
||||
#include "main.h"
|
||||
@@ -35,12 +34,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "plugins/TextMessagePlugin.h"
|
||||
#include "target_specific.h"
|
||||
#include "utils.h"
|
||||
#include "fonts.h"
|
||||
|
||||
using namespace meshtastic; /** @todo remove */
|
||||
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
||||
#define IDLE_FRAMERATE 1 // in fps
|
||||
#define COMPASS_DIAM 44
|
||||
|
||||
// DEBUG
|
||||
#define NUM_EXTRA_FRAMES 3 // text message and debug frame
|
||||
// if defined a pixel will blink to show redraws
|
||||
// #define SHOW_REDRAWS
|
||||
|
||||
// A text message frame + debug frame + all the node infos
|
||||
static FrameCallback normalFrames[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
|
||||
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
||||
@@ -152,6 +161,23 @@ static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
display->drawString(64 + x, 48 + y, buf);
|
||||
}
|
||||
|
||||
/// Draw the last text message we received
|
||||
static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
displayedNodeNum = 0; // Not currently showing a node pane
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
|
||||
char tempBuf[24];
|
||||
snprintf(tempBuf, sizeof(tempBuf), "Critical fault #%d", myNodeInfo.error_code);
|
||||
display->drawString(0 + x, 0 + y, tempBuf);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(0 + x, FONT_HEIGHT_MEDIUM + y, "For help, please post on\nmeshtastic.discourse.group");
|
||||
}
|
||||
|
||||
|
||||
/// Draw the last text message we received
|
||||
static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -849,7 +875,11 @@ void Screen::setFrames()
|
||||
|
||||
size_t numframes = 0;
|
||||
|
||||
// If we have a text message - show it first
|
||||
// If we have a critical fault, show it first
|
||||
if (myNodeInfo.error_code)
|
||||
normalFrames[numframes++] = drawCriticalFaultFrame;
|
||||
|
||||
// If we have a text message - show it next
|
||||
if (devicestate.has_rx_text_message)
|
||||
normalFrames[numframes++] = drawTextMessageFrame;
|
||||
|
||||
@@ -894,9 +924,27 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
void Screen::blink() {
|
||||
setFastFramerate();
|
||||
uint8_t count = 10;
|
||||
dispdev.setBrightness(254);
|
||||
while(count>0) {
|
||||
dispdev.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
dispdev.display();
|
||||
delay(50);
|
||||
dispdev.clear();
|
||||
dispdev.display();
|
||||
delay(50);
|
||||
count = count -1;
|
||||
}
|
||||
dispdev.setBrightness(brightness);
|
||||
}
|
||||
|
||||
void Screen::handlePrint(const char *text)
|
||||
{
|
||||
DEBUG_MSG("Screen: %s", text);
|
||||
// the string passed into us probably has a newline, but that would confuse the logging system
|
||||
// so strip it
|
||||
DEBUG_MSG("Screen: %.*s\n", strlen(text) - 1, text);
|
||||
if (!useDisplay || !showingNormalScreen)
|
||||
return;
|
||||
|
||||
@@ -984,7 +1032,9 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
// The coordinates define the left starting point of the text
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
if (radioConfig.preferences.wifi_ap_mode) {
|
||||
if (isSoftAPForced()) {
|
||||
display->drawString(x, y, String("WiFi: Software AP (Admin)"));
|
||||
} else if (radioConfig.preferences.wifi_ap_mode) {
|
||||
display->drawString(x, y, String("WiFi: Software AP"));
|
||||
} else if (WiFi.status() != WL_CONNECTED) {
|
||||
display->drawString(x, y, String("WiFi: Not Connected"));
|
||||
@@ -1007,13 +1057,15 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
- WL_NO_SHIELD: assigned when no WiFi shield is present;
|
||||
|
||||
*/
|
||||
|
||||
if (WiFi.status() == WL_CONNECTED) {
|
||||
if (radioConfig.preferences.wifi_ap_mode) {
|
||||
if (WiFi.status() == WL_CONNECTED || isSoftAPForced() || radioConfig.preferences.wifi_ap_mode) {
|
||||
if (radioConfig.preferences.wifi_ap_mode || isSoftAPForced()) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.softAPIP().toString().c_str()));
|
||||
} else {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.localIP().toString().c_str()));
|
||||
}
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("(" + String(WiFi.softAPgetStationNum()) + "/4)"),
|
||||
y + FONT_HEIGHT_SMALL * 1, "(" + String(WiFi.softAPgetStationNum()) + "/4)");
|
||||
|
||||
} else if (WiFi.status() == WL_NO_SSID_AVAIL) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "SSID Not Found");
|
||||
} else if (WiFi.status() == WL_CONNECTION_LOST) {
|
||||
@@ -1088,10 +1140,23 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
}
|
||||
}
|
||||
|
||||
if ((millis() / 10000) % 2) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName));
|
||||
if (isSoftAPForced()) {
|
||||
if ((millis() / 10000) % 2) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: meshtasticAdmin");
|
||||
} else {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "PWD: 12345678");
|
||||
}
|
||||
|
||||
} else {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "PWD: " + String(wifiPsw));
|
||||
if (radioConfig.preferences.wifi_ap_mode) {
|
||||
if ((millis() / 10000) % 2) {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName));
|
||||
} else {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "PWD: " + String(wifiPsw));
|
||||
}
|
||||
} else {
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName));
|
||||
}
|
||||
}
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 3, "http://meshtastic.local");
|
||||
|
||||
@@ -1185,8 +1250,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
// DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
|
||||
switch (arg->getStatusType()) {
|
||||
case STATUS_TYPE_NODE:
|
||||
if (showingNormalScreen &&
|
||||
nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
|
||||
if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
|
||||
setFrames(); // Regen the list of screens
|
||||
}
|
||||
nodeDB.updateGUI = false;
|
||||
|
||||
@@ -107,6 +107,8 @@ class Screen : public concurrency::OSThread
|
||||
*/
|
||||
void doDeepSleep();
|
||||
|
||||
void blink();
|
||||
|
||||
/// Handles a button press.
|
||||
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#ifdef ST7735_CS
|
||||
#include "SPILock.h"
|
||||
#include "TFTDisplay.h"
|
||||
#include "graphics/configs.h"
|
||||
#include <SPI.h>
|
||||
#include <TFT_eSPI.h> // Graphics and font library for ST7735 driver chip
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "fonts.h"
|
||||
|
||||
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
||||
#define IDLE_FRAMERATE 1 // in fps
|
||||
#define COMPASS_DIAM 44
|
||||
|
||||
// DEBUG
|
||||
#define NUM_EXTRA_FRAMES 2 // text message and debug frame
|
||||
// if defined a pixel will blink to show redraws
|
||||
// #define SHOW_REDRAWS
|
||||
38
src/main.cpp
38
src/main.cpp
@@ -5,6 +5,7 @@
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "UBloxGPS.h"
|
||||
#include "airtime.h"
|
||||
#include "configuration.h"
|
||||
#include "error.h"
|
||||
#include "power.h"
|
||||
@@ -92,6 +93,7 @@ void scanI2Cdevice(void)
|
||||
DEBUG_MSG("Unknow error at address 0x%x\n", addr);
|
||||
}
|
||||
}
|
||||
|
||||
if (nDevices == 0)
|
||||
DEBUG_MSG("No I2C devices found\n");
|
||||
else
|
||||
@@ -236,11 +238,12 @@ class ButtonThread : public OSThread
|
||||
if (millis() - longPressTime > 10 * 1000) {
|
||||
#ifdef TBEAM_V10
|
||||
if (axp192_found == true) {
|
||||
setLed(false);
|
||||
power->shutdown();
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
//DEBUG_MSG("Long press %u\n", (millis() - longPressTime));
|
||||
// DEBUG_MSG("Long press %u\n", (millis() - longPressTime));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,6 +300,22 @@ void setup()
|
||||
digitalWrite(RESET_OLED, 1);
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
|
||||
// BUTTON_PIN is pulled high by a 12k resistor.
|
||||
if (!digitalRead(BUTTON_PIN)) {
|
||||
forceSoftAP = 1;
|
||||
DEBUG_MSG("-------------------- Setting forceSoftAP = 1\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
OSThread::setup();
|
||||
|
||||
ledPeriodic = new Periodic("Blink", ledBlinker);
|
||||
@@ -345,6 +364,10 @@ void setup()
|
||||
nrf52Setup();
|
||||
#endif
|
||||
|
||||
// We do this as early as possible because this loads preferences from flash
|
||||
// but we need to do this after main cpu iniot (esp32setup), because we need the random seed set
|
||||
nodeDB.init();
|
||||
|
||||
// Currently only the tbeam has a PMU
|
||||
power = new Power();
|
||||
power->setStatusHandler(powerStatus);
|
||||
@@ -403,6 +426,12 @@ void setup()
|
||||
|
||||
service.init();
|
||||
|
||||
// Do this after service.init (because that clears error_code)
|
||||
#ifdef AXP192_SLAVE_ADDRESS
|
||||
if(!axp192_found)
|
||||
recordCriticalError(CriticalErrorCode_NoAXP192); // Record a hardware fault for missing hardware
|
||||
#endif
|
||||
|
||||
// Don't call screen setup until after nodedb is setup (because we need
|
||||
// the current region name)
|
||||
#if defined(ST7735_CS) || defined(HAS_EINK)
|
||||
@@ -466,10 +495,10 @@ void setup()
|
||||
#endif
|
||||
|
||||
// Initialize Wifi
|
||||
initWifi();
|
||||
initWifi(forceSoftAP);
|
||||
|
||||
if (!rIf)
|
||||
recordCriticalError(ErrNoRadio);
|
||||
recordCriticalError(CriticalErrorCode_NoRadio);
|
||||
else
|
||||
router->addInterface(rIf);
|
||||
|
||||
@@ -541,4 +570,7 @@ 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();
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ bool DSRRouter::weAreInRoute(const RouteDiscovery &route)
|
||||
**/
|
||||
void DSRRouter::updateRoutes(const RouteDiscovery &route, bool isRequest)
|
||||
{
|
||||
DEBUG_MSG("FIXME not implemented");
|
||||
DEBUG_MSG("FIXME not implemented updateRoutes\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +178,7 @@ void DSRRouter::updateRoutes(const RouteDiscovery &route, bool isRequest)
|
||||
*/
|
||||
void DSRRouter::sendRouteReply(const RouteDiscovery &route, NodeNum toAppend)
|
||||
{
|
||||
DEBUG_MSG("FIXME not implemented");
|
||||
DEBUG_MSG("FIXME not implemented sendRoute\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +188,7 @@ void DSRRouter::sendRouteReply(const RouteDiscovery &route, NodeNum toAppend)
|
||||
*/
|
||||
NodeNum DSRRouter::getNextHop(NodeNum dest)
|
||||
{
|
||||
DEBUG_MSG("FIXME not implemented");
|
||||
DEBUG_MSG("FIXME not implemented getNextHop\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ NodeNum DSRRouter::getNextHop(NodeNum dest)
|
||||
*/
|
||||
void DSRRouter::resendRouteRequest(const MeshPacket *p)
|
||||
{
|
||||
DEBUG_MSG("FIXME not implemented");
|
||||
DEBUG_MSG("FIXME not implemented resendRoute\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +208,7 @@ void DSRRouter::resendRouteRequest(const MeshPacket *p)
|
||||
*/
|
||||
void DSRRouter::addRoute(NodeNum dest, NodeNum forwarder, uint8_t numHops)
|
||||
{
|
||||
DEBUG_MSG("FIXME not implemented");
|
||||
DEBUG_MSG("FIXME not implemented addRoute\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -216,7 +216,7 @@ void DSRRouter::addRoute(NodeNum dest, NodeNum forwarder, uint8_t numHops)
|
||||
*/
|
||||
void DSRRouter::removeRoute(NodeNum dest)
|
||||
{
|
||||
DEBUG_MSG("FIXME not implemented");
|
||||
DEBUG_MSG("FIXME not implemented removeRoute\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +224,7 @@ void DSRRouter::removeRoute(NodeNum dest)
|
||||
*/
|
||||
void DSRRouter::sendNextHop(NodeNum n, const MeshPacket *p)
|
||||
{
|
||||
DEBUG_MSG("FIXME not implemented");
|
||||
DEBUG_MSG("FIXME not implemented sendNextHop\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,7 +232,7 @@ void DSRRouter::sendNextHop(NodeNum n, const MeshPacket *p)
|
||||
*/
|
||||
void DSRRouter::sendRouteError(const MeshPacket *p, RouteError err)
|
||||
{
|
||||
DEBUG_MSG("FIXME not implemented");
|
||||
DEBUG_MSG("FIXME not implemented sendRouteError\n");
|
||||
}
|
||||
|
||||
/** make a copy of p, start discovery, but only if we don't
|
||||
@@ -241,5 +241,5 @@ void DSRRouter::sendRouteError(const MeshPacket *p, RouteError err)
|
||||
*/
|
||||
void DSRRouter::startDiscovery(NodeNum dest)
|
||||
{
|
||||
DEBUG_MSG("FIXME not implemented");
|
||||
DEBUG_MSG("FIXME not implemented startDiscovery\n");
|
||||
}
|
||||
@@ -20,8 +20,7 @@ ErrorCode FloodingRouter::send(MeshPacket *p)
|
||||
bool FloodingRouter::shouldFilterReceived(const MeshPacket *p)
|
||||
{
|
||||
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
|
||||
DEBUG_MSG("Ignoring incoming msg, because we've already seen it: fr=0x%x,to=0x%x,id=%d,hop_limit=%d\n", p->from, p->to,
|
||||
p->id, p->hop_limit);
|
||||
printPacket("Ignoring incoming msg, because we've already seen it", p);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,8 +37,7 @@ void FloodingRouter::sniffReceived(const MeshPacket *p)
|
||||
|
||||
tosend->hop_limit--; // bump down the hop count
|
||||
|
||||
DEBUG_MSG("Rebroadcasting received floodmsg to neighbors, fr=0x%x,to=0x%x,id=%d,hop_limit=%d\n", p->from, p->to,
|
||||
p->id, tosend->hop_limit);
|
||||
printPacket("Rebroadcasting received floodmsg to neighbors", p);
|
||||
// Note: we are careful to resend using the original senders node id
|
||||
// We are careful not to call our hooked version of send() - because we don't want to check this again
|
||||
Router::send(tosend);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
std::vector<MeshPlugin *> *MeshPlugin::plugins;
|
||||
|
||||
const MeshPacket *MeshPlugin::currentRequest;
|
||||
|
||||
MeshPlugin::MeshPlugin(const char *_name) : name(_name)
|
||||
{
|
||||
// Can't trust static initalizer order, so we check each time
|
||||
@@ -25,23 +27,30 @@ MeshPlugin::~MeshPlugin()
|
||||
void MeshPlugin::callPlugins(const MeshPacket &mp)
|
||||
{
|
||||
// DEBUG_MSG("In call plugins\n");
|
||||
bool pluginFound = false;
|
||||
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
|
||||
auto &pi = **i;
|
||||
|
||||
pi.currentRequest = ∓
|
||||
if (pi.wantPortnum(mp.decoded.data.portnum)) {
|
||||
pluginFound = true;
|
||||
|
||||
bool handled = pi.handleReceived(mp);
|
||||
|
||||
// Possibly send replies (unless we are handling a locally generated message)
|
||||
if (mp.decoded.want_response && mp.from != nodeDB.getNodeNum())
|
||||
// Possibly send replies
|
||||
if (mp.decoded.want_response)
|
||||
pi.sendResponse(mp);
|
||||
|
||||
DEBUG_MSG("Plugin %s handled=%d\n", pi.name, handled);
|
||||
if (handled)
|
||||
break;
|
||||
}
|
||||
else {
|
||||
DEBUG_MSG("Plugin %s not interested\n", pi.name);
|
||||
}
|
||||
|
||||
pi.currentRequest = NULL;
|
||||
}
|
||||
|
||||
if(!pluginFound)
|
||||
DEBUG_MSG("No plugins interested in portnum=%d\n", mp.decoded.data.portnum);
|
||||
}
|
||||
|
||||
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
|
||||
|
||||
@@ -31,6 +31,15 @@ class MeshPlugin
|
||||
protected:
|
||||
const char *name;
|
||||
|
||||
/**
|
||||
* If this plugin is currently handling a request currentRequest will be preset
|
||||
* to the packet with the request. This is mostly useful for reply handlers.
|
||||
*
|
||||
* Note: this can be static because we are guaranteed to be processing only one
|
||||
* plugin at a time.
|
||||
*/
|
||||
static const MeshPacket *currentRequest;
|
||||
|
||||
/**
|
||||
* Initialize your plugin. This setup function is called once after all hardware and mesh protocol layers have
|
||||
* been initialized
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include "MeshTypes.h"
|
||||
#include "PointerQueue.h"
|
||||
#include "configuration.h"
|
||||
#include "mesh.pb.h"
|
||||
|
||||
// Map from old region names to new region enums
|
||||
struct RegionInfo {
|
||||
|
||||
@@ -53,7 +53,14 @@ MeshService service;
|
||||
|
||||
static int32_t sendOwnerCb()
|
||||
{
|
||||
nodeInfoPlugin.sendOurNodeInfo();
|
||||
static uint32_t currentGeneration;
|
||||
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
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)
|
||||
|
||||
return getPref_send_owner_interval() * getPref_position_broadcast_secs() * 1000;
|
||||
}
|
||||
@@ -68,8 +75,10 @@ MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
||||
void MeshService::init()
|
||||
{
|
||||
sendOwnerPeriod = new concurrency::Periodic("SendOwner", sendOwnerCb);
|
||||
sendOwnerPeriod->setIntervalFromNow(30 * 1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup)
|
||||
|
||||
nodeDB.init();
|
||||
// moved much earlier in boot (called from setup())
|
||||
// nodeDB.init();
|
||||
|
||||
if (gps)
|
||||
gpsObserver.observe(&gps->newStatus);
|
||||
@@ -230,8 +239,15 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *unused)
|
||||
uint32_t now = millis();
|
||||
if (lastGpsSend == 0 || now - lastGpsSend > getPref_position_broadcast_secs() * 1000) {
|
||||
lastGpsSend = now;
|
||||
DEBUG_MSG("Sending position to mesh (not requesting replies)\n");
|
||||
positionPlugin.sendOurPosition();
|
||||
|
||||
static uint32_t currentGeneration;
|
||||
|
||||
// If we changed channels, ask everyone else for their latest info
|
||||
bool requestReplies = currentGeneration != radioGeneration;
|
||||
currentGeneration = radioGeneration;
|
||||
|
||||
DEBUG_MSG("Sending position to mesh (wantReplies=%d)\n", requestReplies);
|
||||
positionPlugin.sendOurPosition(NODENUM_BROADCAST, requestReplies);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "MeshTypes.h"
|
||||
#include "Observer.h"
|
||||
#include "PointerQueue.h"
|
||||
#include "mesh.pb.h"
|
||||
|
||||
/**
|
||||
* Top level app for this service. keeps the mesh, the radio config and the queue of received packets.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// low level types
|
||||
|
||||
#include "MemoryPool.h"
|
||||
#include "mesh.pb.h"
|
||||
#include "mesh/mesh-pb-constants.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
typedef uint32_t NodeNum;
|
||||
|
||||
@@ -28,6 +28,11 @@ 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
|
||||
* might have changed is incremented. Allows others to detect they might now be on a new channel.
|
||||
*/
|
||||
uint32_t radioGeneration;
|
||||
|
||||
/*
|
||||
DeviceState versions used to be defined in the .proto file but really only this function cares. So changed to a
|
||||
#define here.
|
||||
@@ -57,16 +62,24 @@ static uint8_t ourMacAddr[6];
|
||||
*/
|
||||
NodeNum displayedNodeNum;
|
||||
|
||||
/// A usable (but bigger) version of the channel name in the channelSettings object
|
||||
const char *channelName;
|
||||
|
||||
/// A usable psk - which has been constructed based on the (possibly short psk) in channelSettings
|
||||
static uint8_t activePSK[32];
|
||||
static uint8_t activePSKSize;
|
||||
|
||||
/**
|
||||
* Generate a short suffix used to disambiguate channels that might have the same "name" entered by the human but different PSKs.
|
||||
* The ideas is that the PSK changing should be visible to the user so that they see they probably messed up and that's why they
|
||||
their nodes
|
||||
* aren't talking to each other.
|
||||
*
|
||||
* This string is of the form "#name-XY".
|
||||
* This string is of the form "#name-X".
|
||||
*
|
||||
* Where X is a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together.
|
||||
* Y is not yet used but should eventually indicate 'speed/range' of the link
|
||||
* Where X is either:
|
||||
* (for custom PSKS) a letter from A to Z (base26), and formed by xoring all the bytes of the PSK together,
|
||||
* OR (for the standard minimially secure PSKs) a number from 0 to 9.
|
||||
*
|
||||
* This function will also need to be implemented in GUI apps that talk to the radio.
|
||||
*
|
||||
@@ -76,11 +89,19 @@ const char *getChannelName()
|
||||
{
|
||||
static char buf[32];
|
||||
|
||||
uint8_t code = 0;
|
||||
for (int i = 0; i < channelSettings.psk.size; i++)
|
||||
code ^= channelSettings.psk.bytes[i];
|
||||
char suffix;
|
||||
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++)
|
||||
code ^= activePSK[i];
|
||||
|
||||
snprintf(buf, sizeof(buf), "#%s-%c", channelSettings.name, 'A' + (code % 26));
|
||||
suffix = 'A' + (code % 26);
|
||||
} else {
|
||||
suffix = '0' + channelSettings.psk.bytes[0];
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "#%s-%c", channelName, suffix);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -90,6 +111,8 @@ bool NodeDB::resetRadioConfig()
|
||||
{
|
||||
bool didFactoryReset = false;
|
||||
|
||||
radioGeneration++;
|
||||
|
||||
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
|
||||
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
|
||||
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
|
||||
@@ -110,13 +133,62 @@ bool NodeDB::resetRadioConfig()
|
||||
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
|
||||
|
||||
channelSettings.tx_power = 0; // default
|
||||
memcpy(&channelSettings.psk.bytes, defaultpsk, sizeof(channelSettings.psk));
|
||||
channelSettings.psk.size = sizeof(defaultpsk);
|
||||
strcpy(channelSettings.name, "Default");
|
||||
uint8_t defaultpskIndex = 1;
|
||||
channelSettings.psk.bytes[0] = defaultpskIndex;
|
||||
channelSettings.psk.size = 1;
|
||||
strcpy(channelSettings.name, "");
|
||||
}
|
||||
|
||||
// Convert the old string "Default" to our new short representation
|
||||
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
|
||||
// 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) {
|
||||
case ChannelSettings_ModemConfig_Bw125Cr45Sf128:
|
||||
channelName = "Medium"; break;
|
||||
case ChannelSettings_ModemConfig_Bw500Cr45Sf128:
|
||||
channelName = "ShortFast"; break;
|
||||
case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512:
|
||||
channelName = "LongAlt"; break;
|
||||
case ChannelSettings_ModemConfig_Bw125Cr48Sf4096:
|
||||
channelName = "LongSlow"; break;
|
||||
default:
|
||||
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) {
|
||||
*channelSettings.psk.bytes = 1;
|
||||
channelSettings.psk.size = 1;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
uint8_t pskIndex = activePSK[0];
|
||||
DEBUG_MSG("Expanding short PSK #%d\n", pskIndex);
|
||||
if(pskIndex == 0)
|
||||
activePSKSize = 0; // Turn off encryption
|
||||
else {
|
||||
memcpy(activePSK, defaultpsk, sizeof(defaultpsk));
|
||||
activePSKSize = sizeof(defaultpsk);
|
||||
// Bump up the last byte of PSK as needed
|
||||
uint8_t *last = activePSK + sizeof(defaultpsk) - 1;
|
||||
*last = *last + pskIndex - 1; // index of 1 means no change vs defaultPSK
|
||||
}
|
||||
}
|
||||
|
||||
// Tell our crypto engine about the psk
|
||||
crypto->setKey(channelSettings.psk.size, channelSettings.psk.bytes);
|
||||
crypto->setKey(activePSKSize, activePSK);
|
||||
|
||||
// temp hack for quicker testing
|
||||
// devicestate.no_save = true;
|
||||
@@ -199,6 +271,9 @@ 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_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
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ extern MyNodeInfo &myNodeInfo;
|
||||
extern RadioConfig &radioConfig;
|
||||
extern ChannelSettings &channelSettings;
|
||||
extern User &owner;
|
||||
extern const char *channelName;
|
||||
|
||||
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
|
||||
uint32_t sinceLastSeen(const NodeInfo *n);
|
||||
@@ -146,23 +147,58 @@ their nodes
|
||||
*/
|
||||
const char *getChannelName();
|
||||
|
||||
/*
|
||||
If is_router is set, we use a number of different default values
|
||||
|
||||
# FIXME - after tuning, move these params into the on-device defaults based on is_router and is_low_power
|
||||
|
||||
# prefs.position_broadcast_secs = FIXME possibly broadcast only once an hr
|
||||
prefs.wait_bluetooth_secs = 1 # Don't stay in bluetooth mode
|
||||
prefs.mesh_sds_timeout_secs = never
|
||||
prefs.phone_sds_timeout_sec = never
|
||||
# try to stay in light sleep one full day, then briefly wake and sleep again
|
||||
|
||||
prefs.ls_secs = oneday
|
||||
|
||||
prefs.send_owner_interval = 2 # Send an owner packet every other network ping
|
||||
prefs.position_broadcast_secs = 12 hours # send either position or owner every 12hrs
|
||||
|
||||
# get a new GPS position once per day
|
||||
prefs.gps_update_interval = oneday
|
||||
|
||||
prefs.is_low_power = True
|
||||
|
||||
# allow up to five minutes for each new GPS lock attempt
|
||||
prefs.gps_attempt_time = 300
|
||||
*/
|
||||
|
||||
// Our delay functions check for this for times that should never expire
|
||||
#define DELAY_FOREVER 0xffffffff
|
||||
|
||||
#define IF_ROUTER(routerVal, normalVal) (radioConfig.preferences.is_router ? (routerVal) : (normalVal))
|
||||
|
||||
#define PREF_GET(name, defaultVal) \
|
||||
inline uint32_t getPref_##name() { return radioConfig.preferences.name ? radioConfig.preferences.name : (defaultVal); }
|
||||
|
||||
PREF_GET(send_owner_interval, 4)
|
||||
PREF_GET(position_broadcast_secs, 15 * 60)
|
||||
PREF_GET(send_owner_interval, IF_ROUTER(2, 4))
|
||||
PREF_GET(position_broadcast_secs, IF_ROUTER(12 * 60 * 60, 15 * 60))
|
||||
|
||||
// Each time we wake into the DARK state allow 1 minute to send and receive BLE packets to the phone
|
||||
PREF_GET(wait_bluetooth_secs, 60)
|
||||
PREF_GET(wait_bluetooth_secs, IF_ROUTER(1, 60))
|
||||
|
||||
PREF_GET(screen_on_secs, 60)
|
||||
PREF_GET(mesh_sds_timeout_secs, 2 * 60 * 60)
|
||||
PREF_GET(phone_sds_timeout_sec, 2 * 60 * 60)
|
||||
PREF_GET(mesh_sds_timeout_secs, IF_ROUTER(DELAY_FOREVER, 2 * 60 * 60))
|
||||
PREF_GET(phone_sds_timeout_sec, IF_ROUTER(DELAY_FOREVER, 2 * 60 * 60))
|
||||
PREF_GET(sds_secs, 365 * 24 * 60 * 60)
|
||||
|
||||
// We default to sleeping (with bluetooth off for 5 minutes at a time). This seems to be a good tradeoff between
|
||||
// latency for the user sending messages and power savings because of not having to run (expensive) ESP32 bluetooth
|
||||
PREF_GET(ls_secs, 5 * 60)
|
||||
PREF_GET(ls_secs, IF_ROUTER(24 * 60 * 60, 5 * 60))
|
||||
|
||||
PREF_GET(phone_timeout_secs, 15 * 60)
|
||||
PREF_GET(min_wake_secs, 10)
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
extern uint32_t radioGeneration;
|
||||
|
||||
@@ -6,17 +6,28 @@
|
||||
#include "RadioInterface.h"
|
||||
#include <assert.h>
|
||||
|
||||
PhoneAPI::PhoneAPI()
|
||||
{
|
||||
assert(FromRadio_size <= MAX_TO_FROM_RADIO_SIZE);
|
||||
assert(ToRadio_size <= MAX_TO_FROM_RADIO_SIZE);
|
||||
}
|
||||
#if FromRadio_size > MAX_TO_FROM_RADIO_SIZE
|
||||
#error FromRadio is too big
|
||||
#endif
|
||||
|
||||
#if ToRadio_size > MAX_TO_FROM_RADIO_SIZE
|
||||
#error ToRadio is too big
|
||||
#endif
|
||||
|
||||
PhoneAPI::PhoneAPI() {}
|
||||
|
||||
void PhoneAPI::init()
|
||||
{
|
||||
observe(&service.fromNumChanged);
|
||||
}
|
||||
|
||||
void PhoneAPI::close() {
|
||||
unobserve();
|
||||
state = STATE_SEND_NOTHING;
|
||||
isConnected = false;
|
||||
onConnectionChanged(isConnected);
|
||||
}
|
||||
|
||||
void PhoneAPI::checkConnectionTimeout()
|
||||
{
|
||||
if (isConnected) {
|
||||
@@ -95,11 +106,11 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
{
|
||||
if (!available()) {
|
||||
// DEBUG_MSG("getFromRadio, !available\n");
|
||||
return false;
|
||||
} else {
|
||||
DEBUG_MSG("getFromRadio, state=%d\n", state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEBUG_MSG("getFromRadio, state=%d\n", state);
|
||||
|
||||
// In case we send a FromRadio packet
|
||||
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
||||
|
||||
@@ -162,6 +173,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
case STATE_SEND_PACKETS:
|
||||
// Do we have a message from the mesh?
|
||||
if (packetForPhone) {
|
||||
|
||||
printPacket("phone downloaded packet", packetForPhone);
|
||||
|
||||
// Encapsulate as a FromRadio packet
|
||||
fromRadioScratch.which_variant = FromRadio_packet_tag;
|
||||
fromRadioScratch.variant.packet = *packetForPhone;
|
||||
@@ -212,11 +226,14 @@ bool PhoneAPI::available()
|
||||
return true;
|
||||
|
||||
case STATE_LEGACY: // Treat as the same as send packets
|
||||
case STATE_SEND_PACKETS:
|
||||
case STATE_SEND_PACKETS: {
|
||||
// Try to pull a new packet from the service (if we haven't already)
|
||||
if (!packetForPhone)
|
||||
packetForPhone = service.getForPhone();
|
||||
return !!packetForPhone;
|
||||
bool hasPacket = !!packetForPhone;
|
||||
// DEBUG_MSG("available hasPacket=%d\n", hasPacket);
|
||||
return hasPacket;
|
||||
}
|
||||
|
||||
default:
|
||||
assert(0); // unexpected state - FIXME, make an error code and reboot
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "Observer.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include "mesh.pb.h"
|
||||
#include <string>
|
||||
|
||||
// Make sure that we never let our packets grow too large for one BLE packet
|
||||
@@ -21,7 +20,7 @@ class PhoneAPI
|
||||
: public Observer<uint32_t> // FIXME, we shouldn't be inheriting from Observer, instead use CallbackObserver as a member
|
||||
{
|
||||
enum State {
|
||||
STATE_LEGACY, // Temporary default state - until Android apps are all updated, uses the old BLE API
|
||||
STATE_LEGACY, // (no longer used) old default state - until Android apps are all updated, uses the old BLE API
|
||||
STATE_SEND_NOTHING, // (Eventual) Initial state, don't send anything until the client starts asking for config
|
||||
STATE_SEND_MY_INFO, // send our my info record
|
||||
STATE_SEND_RADIO,
|
||||
@@ -31,7 +30,7 @@ class PhoneAPI
|
||||
STATE_SEND_PACKETS // send packets or debug strings
|
||||
};
|
||||
|
||||
State state = STATE_LEGACY;
|
||||
State state = STATE_SEND_NOTHING;
|
||||
|
||||
/**
|
||||
* Each packet sent to the phone has an incrementing count
|
||||
@@ -53,14 +52,16 @@ class PhoneAPI
|
||||
/** the last msec we heard from the client on the other side of this link */
|
||||
uint32_t lastContactMsec = 0;
|
||||
|
||||
bool isConnected = false;
|
||||
|
||||
public:
|
||||
PhoneAPI();
|
||||
|
||||
/// Do late init that can't happen at constructor time
|
||||
virtual void init();
|
||||
|
||||
// Call this when the client drops the connection, resets the state to STATE_SEND_NOTHING
|
||||
// Unregisters our observer
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Handle a ToRadio protobuf
|
||||
*/
|
||||
@@ -87,6 +88,9 @@ class PhoneAPI
|
||||
void handleSetRadio(const RadioConfig &r);
|
||||
|
||||
protected:
|
||||
/// Are we currently connected to a client?
|
||||
bool isConnected = false;
|
||||
|
||||
/// Our fromradio packet while it is being assembled
|
||||
FromRadio fromRadioScratch;
|
||||
|
||||
|
||||
@@ -55,11 +55,11 @@ template <class T> class ProtobufPlugin : private SinglePortPlugin
|
||||
// it would be better to update even if the message was destined to others.
|
||||
|
||||
auto &p = mp.decoded.data;
|
||||
DEBUG_MSG("Received %s from=0x%0x, id=%d, payloadlen=%d\n", name, mp.from, mp.id, p.payload.size);
|
||||
DEBUG_MSG("Received %s from=0x%0x, id=0x%x, payloadlen=%d\n", name, mp.from, mp.id, p.payload.size);
|
||||
|
||||
T scratch;
|
||||
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
|
||||
handleReceivedProtobuf(mp, scratch);
|
||||
return handleReceivedProtobuf(mp, scratch);
|
||||
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "RF95Interface.h"
|
||||
#include "MeshRadio.h" // kinda yucky, but we need to know which region we are in
|
||||
#include "RadioLibRF95.h"
|
||||
#include "error.h"
|
||||
#include <configuration.h>
|
||||
|
||||
#define MAX_POWER 20
|
||||
@@ -85,6 +86,8 @@ void INTERRUPT_ATTR RF95Interface::disableInterrupt()
|
||||
lora->clearDio0Action();
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool RF95Interface::reconfigure()
|
||||
{
|
||||
applyModemConfig();
|
||||
@@ -94,13 +97,13 @@ bool RF95Interface::reconfigure()
|
||||
|
||||
// configure publicly accessible settings
|
||||
int err = lora->setSpreadingFactor(sf);
|
||||
assert(err == ERR_NONE);
|
||||
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
|
||||
|
||||
err = lora->setBandwidth(bw);
|
||||
assert(err == ERR_NONE);
|
||||
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
|
||||
|
||||
err = lora->setCodingRate(cr);
|
||||
assert(err == ERR_NONE);
|
||||
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
|
||||
|
||||
err = lora->setSyncWord(syncWord);
|
||||
assert(err == ERR_NONE);
|
||||
@@ -112,12 +115,12 @@ bool RF95Interface::reconfigure()
|
||||
assert(err == ERR_NONE);
|
||||
|
||||
err = lora->setFrequency(freq);
|
||||
assert(err == ERR_NONE);
|
||||
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
|
||||
|
||||
if (power > MAX_POWER) // This chip has lower power limits than some
|
||||
power = MAX_POWER;
|
||||
err = lora->setOutputPower(power);
|
||||
assert(err == ERR_NONE);
|
||||
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
|
||||
|
||||
startReceive(); // restart receiving
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
||||
auto &s = p->decoded;
|
||||
switch (s.which_payload) {
|
||||
case SubPacket_data_tag:
|
||||
DEBUG_MSG(" Payload:Data");
|
||||
DEBUG_MSG(" Portnum=%d", s.data.portnum);
|
||||
break;
|
||||
case SubPacket_position_tag:
|
||||
DEBUG_MSG(" Payload:Position");
|
||||
@@ -262,10 +262,10 @@ void RadioInterface::applyModemConfig()
|
||||
|
||||
// If user has manually specified a channel num, then use that, otherwise generate one by hashing the name
|
||||
int channel_num =
|
||||
(channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelSettings.name)) % myRegion->numChannels;
|
||||
(channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelName)) % myRegion->numChannels;
|
||||
freq = myRegion->freq + myRegion->spacing * channel_num;
|
||||
|
||||
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelSettings.name, channelSettings.modem_config, channel_num,
|
||||
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelName, channelSettings.modem_config, channel_num,
|
||||
power);
|
||||
DEBUG_MSG("Radio myRegion->freq: %f\n", myRegion->freq);
|
||||
DEBUG_MSG("Radio myRegion->spacing: %f\n", myRegion->spacing);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "MeshTypes.h"
|
||||
#include "Observer.h"
|
||||
#include "PointerQueue.h"
|
||||
#include "mesh.pb.h"
|
||||
#include "airtime.h"
|
||||
|
||||
#define MAX_TX_QUEUE 16 // max number of packets which can be waiting for transmission
|
||||
|
||||
@@ -36,7 +36,7 @@ typedef struct {
|
||||
*
|
||||
* This defines the SOLE API for talking to radios (because soon we will have alternate radio implementations)
|
||||
*/
|
||||
class RadioInterface
|
||||
class RadioInterface
|
||||
{
|
||||
friend class MeshRadio; // for debugging we let that class touch pool
|
||||
PointerQueue<MeshPacket> *rxDest = NULL;
|
||||
|
||||
@@ -58,7 +58,6 @@ void INTERRUPT_ATTR RadioLibInterface::isrTxLevel0()
|
||||
*/
|
||||
RadioLibInterface *RadioLibInterface::instance;
|
||||
|
||||
|
||||
/** Could we send right now (i.e. either not actively receving or transmitting)? */
|
||||
bool RadioLibInterface::canSendImmediately()
|
||||
{
|
||||
@@ -96,6 +95,10 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
|
||||
return res;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
startTransmitTimer(true);
|
||||
@@ -205,12 +208,16 @@ void RadioLibInterface::completeSending()
|
||||
|
||||
void RadioLibInterface::handleReceiveInterrupt()
|
||||
{
|
||||
uint32_t xmitMsec;
|
||||
assert(isReceiving);
|
||||
isReceiving = false;
|
||||
|
||||
// read the number of actually received bytes
|
||||
size_t length = iface->getPacketLength();
|
||||
|
||||
xmitMsec = getPacketTime(length);
|
||||
logAirtime(RX_ALL_LOG, xmitMsec);
|
||||
|
||||
int state = iface->readData(radiobuf, length);
|
||||
if (state != ERR_NONE) {
|
||||
DEBUG_MSG("ignoring received packet due to error=%d\n", state);
|
||||
@@ -250,11 +257,14 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
|
||||
printPacket("Lora RX", mp);
|
||||
|
||||
xmitMsec = getPacketTime(mp);
|
||||
logAirtime(RX_LOG, xmitMsec);
|
||||
|
||||
deliverToReceiver(mp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** start an immediate transmit */
|
||||
void RadioLibInterface::startSend(MeshPacket *txp)
|
||||
{
|
||||
|
||||
@@ -92,7 +92,7 @@ void ReliableRouter::sendAckNak(bool isAck, NodeNum to, PacketId idFrom)
|
||||
auto p = allocForSending();
|
||||
p->hop_limit = 0; // Assume just immediate neighbors for now
|
||||
p->to = to;
|
||||
DEBUG_MSG("Sending an ack=0x%x,to=0x%x,idFrom=%d,id=%d\n", isAck, to, idFrom, p->id);
|
||||
DEBUG_MSG("Sending an ack=0x%x,to=0x%x,idFrom=0x%x,id=0x%x\n", isAck, to, idFrom, p->id);
|
||||
|
||||
if (isAck) {
|
||||
p->decoded.ack.success_id = idFrom;
|
||||
|
||||
@@ -93,19 +93,28 @@ MeshPacket *Router::allocForSending()
|
||||
p->to = NODENUM_BROADCAST;
|
||||
p->hop_limit = HOP_RELIABLE;
|
||||
p->id = generatePacketId();
|
||||
p->rx_time = getValidTime(RTCQualityFromNet); // Just in case we process the packet locally - make sure it has a valid timestamp
|
||||
p->rx_time =
|
||||
getValidTime(RTCQualityFromNet); // Just in case we process the packet locally - make sure it has a valid timestamp
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
ErrorCode Router::sendLocal(MeshPacket *p)
|
||||
{
|
||||
// No need to deliver externally if the destination is the local node
|
||||
if (p->to == nodeDB.getNodeNum()) {
|
||||
DEBUG_MSG("Enqueuing internal message for the receive queue\n");
|
||||
printPacket("Enqueuing local", p);
|
||||
fromRadioQueue.enqueue(p);
|
||||
return ERRNO_OK;
|
||||
} else
|
||||
return send(p);
|
||||
}
|
||||
|
||||
// If we are sending a broadcast, we also treat it as if we just received it ourself
|
||||
// this allows local apps (and PCs) to see broadcasts sourced locally
|
||||
if (p->to == NODENUM_BROADCAST) {
|
||||
handleReceived(p);
|
||||
}
|
||||
|
||||
return send(p);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "PointerQueue.h"
|
||||
#include "RadioInterface.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "mesh.pb.h"
|
||||
|
||||
/**
|
||||
* A mesh aware router that supports multiple interfaces.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "SX1262Interface.h"
|
||||
#include "error.h"
|
||||
#include <configuration.h>
|
||||
|
||||
// Particular boards might define a different max power based on what their hardware can do
|
||||
@@ -78,13 +79,13 @@ bool SX1262Interface::reconfigure()
|
||||
|
||||
// configure publicly accessible settings
|
||||
int err = lora.setSpreadingFactor(sf);
|
||||
assert(err == ERR_NONE);
|
||||
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
|
||||
|
||||
err = lora.setBandwidth(bw);
|
||||
assert(err == ERR_NONE);
|
||||
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
|
||||
|
||||
err = lora.setCodingRate(cr);
|
||||
assert(err == ERR_NONE);
|
||||
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
|
||||
|
||||
// Hmm - seems to lower SNR when the signal levels are high. Leaving off for now...
|
||||
err = lora.setRxGain(true);
|
||||
@@ -100,7 +101,7 @@ bool SX1262Interface::reconfigure()
|
||||
assert(err == ERR_NONE);
|
||||
|
||||
err = lora.setFrequency(freq);
|
||||
assert(err == ERR_NONE);
|
||||
if(err != ERR_NONE) recordCriticalError(CriticalErrorCode_InvalidRadioSetting);
|
||||
|
||||
if (power > 22) // This chip has lower power limits than some
|
||||
power = 22;
|
||||
|
||||
12
src/mesh/generated/apponly.pb.c
Normal file
12
src/mesh/generated/apponly.pb.c
Normal file
@@ -0,0 +1,12 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#include "apponly.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
PB_BIND(ServiceEnvelope, ServiceEnvelope, 2)
|
||||
|
||||
|
||||
|
||||
56
src/mesh/generated/apponly.pb.h
Normal file
56
src/mesh/generated/apponly.pb.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#ifndef PB_APPONLY_PB_H_INCLUDED
|
||||
#define PB_APPONLY_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
#include "mesh.pb.h"
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _ServiceEnvelope {
|
||||
bool has_packet;
|
||||
MeshPacket packet;
|
||||
pb_callback_t channel_id;
|
||||
pb_callback_t gateway_id;
|
||||
} ServiceEnvelope;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define ServiceEnvelope_init_default {false, MeshPacket_init_default, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||
#define ServiceEnvelope_init_zero {false, MeshPacket_init_zero, {{NULL}, NULL}, {{NULL}, NULL}}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define ServiceEnvelope_packet_tag 1
|
||||
#define ServiceEnvelope_channel_id_tag 2
|
||||
#define ServiceEnvelope_gateway_id_tag 3
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define ServiceEnvelope_FIELDLIST(X, a) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, packet, 1) \
|
||||
X(a, CALLBACK, SINGULAR, STRING, channel_id, 2) \
|
||||
X(a, CALLBACK, SINGULAR, STRING, gateway_id, 3)
|
||||
#define ServiceEnvelope_CALLBACK pb_default_field_callback
|
||||
#define ServiceEnvelope_DEFAULT NULL
|
||||
#define ServiceEnvelope_packet_MSGTYPE MeshPacket
|
||||
|
||||
extern const pb_msgdesc_t ServiceEnvelope_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define ServiceEnvelope_fields &ServiceEnvelope_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
/* ServiceEnvelope_size depends on runtime parameters */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
12
src/mesh/generated/deviceonly.pb.c
Normal file
12
src/mesh/generated/deviceonly.pb.c
Normal file
@@ -0,0 +1,12 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#include "deviceonly.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
PB_BIND(DeviceState, DeviceState, 2)
|
||||
|
||||
|
||||
|
||||
89
src/mesh/generated/deviceonly.pb.h
Normal file
89
src/mesh/generated/deviceonly.pb.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#ifndef PB_DEVICEONLY_PB_H_INCLUDED
|
||||
#define PB_DEVICEONLY_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
#include "mesh.pb.h"
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Struct definitions */
|
||||
typedef struct _DeviceState {
|
||||
bool has_radio;
|
||||
RadioConfig radio;
|
||||
bool has_my_node;
|
||||
MyNodeInfo my_node;
|
||||
bool has_owner;
|
||||
User owner;
|
||||
pb_size_t node_db_count;
|
||||
NodeInfo node_db[32];
|
||||
pb_size_t receive_queue_count;
|
||||
MeshPacket receive_queue[1];
|
||||
bool has_rx_text_message;
|
||||
MeshPacket rx_text_message;
|
||||
uint32_t version;
|
||||
bool no_save;
|
||||
bool did_gps_reset;
|
||||
pb_size_t secondary_channels_count;
|
||||
ChannelSettings secondary_channels[7];
|
||||
} DeviceState;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0, 0, {ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default, ChannelSettings_init_default}}
|
||||
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0, 0, {ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero, ChannelSettings_init_zero}}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define DeviceState_radio_tag 1
|
||||
#define DeviceState_my_node_tag 2
|
||||
#define DeviceState_owner_tag 3
|
||||
#define DeviceState_node_db_tag 4
|
||||
#define DeviceState_receive_queue_tag 5
|
||||
#define DeviceState_rx_text_message_tag 7
|
||||
#define DeviceState_version_tag 8
|
||||
#define DeviceState_no_save_tag 9
|
||||
#define DeviceState_did_gps_reset_tag 11
|
||||
#define DeviceState_secondary_channels_tag 12
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define DeviceState_FIELDLIST(X, a) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, radio, 1) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, my_node, 2) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, version, 8) \
|
||||
X(a, STATIC, SINGULAR, BOOL, no_save, 9) \
|
||||
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, secondary_channels, 12)
|
||||
#define DeviceState_CALLBACK NULL
|
||||
#define DeviceState_DEFAULT NULL
|
||||
#define DeviceState_radio_MSGTYPE RadioConfig
|
||||
#define DeviceState_my_node_MSGTYPE MyNodeInfo
|
||||
#define DeviceState_owner_MSGTYPE User
|
||||
#define DeviceState_node_db_MSGTYPE NodeInfo
|
||||
#define DeviceState_receive_queue_MSGTYPE MeshPacket
|
||||
#define DeviceState_rx_text_message_MSGTYPE MeshPacket
|
||||
#define DeviceState_secondary_channels_MSGTYPE ChannelSettings
|
||||
|
||||
extern const pb_msgdesc_t DeviceState_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define DeviceState_fields &DeviceState_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define DeviceState_size 6176
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.1 */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#include "mesh.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
@@ -39,10 +39,7 @@ PB_BIND(NodeInfo, NodeInfo, AUTO)
|
||||
PB_BIND(MyNodeInfo, MyNodeInfo, AUTO)
|
||||
|
||||
|
||||
PB_BIND(DeviceState, DeviceState, 2)
|
||||
|
||||
|
||||
PB_BIND(DebugString, DebugString, 2)
|
||||
PB_BIND(LogRecord, LogRecord, AUTO)
|
||||
|
||||
|
||||
PB_BIND(FromRadio, FromRadio, 2)
|
||||
@@ -58,3 +55,5 @@ PB_BIND(ToRadio, ToRadio, 2)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.1 */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#ifndef PB_MESH_PB_H_INCLUDED
|
||||
#define PB_MESH_PB_H_INCLUDED
|
||||
@@ -10,10 +10,6 @@
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Enum definitions */
|
||||
typedef enum _RouteError {
|
||||
RouteError_NONE = 0,
|
||||
@@ -52,6 +48,17 @@ typedef enum _LocationSharing {
|
||||
LocationSharing_LocDisabled = 2
|
||||
} LocationSharing;
|
||||
|
||||
typedef enum _CriticalErrorCode {
|
||||
CriticalErrorCode_None = 0,
|
||||
CriticalErrorCode_TxWatchdog = 1,
|
||||
CriticalErrorCode_SleepEnterWait = 2,
|
||||
CriticalErrorCode_NoRadio = 3,
|
||||
CriticalErrorCode_Unspecified = 4,
|
||||
CriticalErrorCode_UBloxInitFailed = 5,
|
||||
CriticalErrorCode_NoAXP192 = 6,
|
||||
CriticalErrorCode_InvalidRadioSetting = 7
|
||||
} CriticalErrorCode;
|
||||
|
||||
typedef enum _ChannelSettings_ModemConfig {
|
||||
ChannelSettings_ModemConfig_Bw125Cr45Sf128 = 0,
|
||||
ChannelSettings_ModemConfig_Bw500Cr45Sf128 = 1,
|
||||
@@ -59,6 +66,16 @@ typedef enum _ChannelSettings_ModemConfig {
|
||||
ChannelSettings_ModemConfig_Bw125Cr48Sf4096 = 3
|
||||
} ChannelSettings_ModemConfig;
|
||||
|
||||
typedef enum _LogRecord_Level {
|
||||
LogRecord_Level_UNSET = 0,
|
||||
LogRecord_Level_CRITICAL = 50,
|
||||
LogRecord_Level_ERROR = 40,
|
||||
LogRecord_Level_WARNING = 30,
|
||||
LogRecord_Level_INFO = 20,
|
||||
LogRecord_Level_DEBUG = 10,
|
||||
LogRecord_Level_TRACE = 5
|
||||
} LogRecord_Level;
|
||||
|
||||
/* Struct definitions */
|
||||
typedef PB_BYTES_ARRAY_T(32) ChannelSettings_psk_t;
|
||||
typedef struct _ChannelSettings {
|
||||
@@ -70,6 +87,9 @@ typedef struct _ChannelSettings {
|
||||
uint32_t spread_factor;
|
||||
uint32_t coding_rate;
|
||||
uint32_t channel_num;
|
||||
uint32_t id;
|
||||
bool uplink_enabled;
|
||||
bool downlink_enabled;
|
||||
} ChannelSettings;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(240) Data_payload_t;
|
||||
@@ -78,9 +98,12 @@ typedef struct _Data {
|
||||
Data_payload_t payload;
|
||||
} Data;
|
||||
|
||||
typedef struct _DebugString {
|
||||
char message[256];
|
||||
} DebugString;
|
||||
typedef struct _LogRecord {
|
||||
char message[64];
|
||||
uint32_t time;
|
||||
char source[8];
|
||||
LogRecord_Level level;
|
||||
} LogRecord;
|
||||
|
||||
typedef struct _MyNodeInfo {
|
||||
uint32_t my_node_num;
|
||||
@@ -89,7 +112,7 @@ typedef struct _MyNodeInfo {
|
||||
char region[12];
|
||||
char hw_model[16];
|
||||
char firmware_version[12];
|
||||
uint32_t error_code;
|
||||
CriticalErrorCode error_code;
|
||||
uint32_t error_address;
|
||||
uint32_t error_count;
|
||||
uint32_t packet_id_bits;
|
||||
@@ -110,7 +133,6 @@ typedef struct _Position {
|
||||
typedef struct _RadioConfig_UserPreferences {
|
||||
uint32_t position_broadcast_secs;
|
||||
uint32_t send_owner_interval;
|
||||
uint32_t num_missed_to_fail;
|
||||
uint32_t wait_bluetooth_secs;
|
||||
uint32_t screen_on_secs;
|
||||
uint32_t phone_timeout_secs;
|
||||
@@ -195,6 +217,7 @@ typedef struct _MeshPacket {
|
||||
SubPacket decoded;
|
||||
MeshPacket_encrypted_t encrypted;
|
||||
};
|
||||
uint32_t channel_index;
|
||||
uint32_t id;
|
||||
float rx_snr;
|
||||
uint32_t rx_time;
|
||||
@@ -202,24 +225,6 @@ typedef struct _MeshPacket {
|
||||
bool want_ack;
|
||||
} MeshPacket;
|
||||
|
||||
typedef struct _DeviceState {
|
||||
bool has_radio;
|
||||
RadioConfig radio;
|
||||
bool has_my_node;
|
||||
MyNodeInfo my_node;
|
||||
bool has_owner;
|
||||
User owner;
|
||||
pb_size_t node_db_count;
|
||||
NodeInfo node_db[32];
|
||||
pb_size_t receive_queue_count;
|
||||
MeshPacket receive_queue[1];
|
||||
bool has_rx_text_message;
|
||||
MeshPacket rx_text_message;
|
||||
uint32_t version;
|
||||
bool no_save;
|
||||
bool did_gps_reset;
|
||||
} DeviceState;
|
||||
|
||||
typedef struct _FromRadio {
|
||||
uint32_t num;
|
||||
pb_size_t which_variant;
|
||||
@@ -228,9 +233,10 @@ typedef struct _FromRadio {
|
||||
MyNodeInfo my_info;
|
||||
NodeInfo node_info;
|
||||
RadioConfig radio;
|
||||
DebugString debug_string;
|
||||
LogRecord log_record;
|
||||
uint32_t config_complete_id;
|
||||
bool rebooted;
|
||||
ChannelSettings channel;
|
||||
} variant;
|
||||
} FromRadio;
|
||||
|
||||
@@ -241,6 +247,7 @@ typedef struct _ToRadio {
|
||||
uint32_t want_config_id;
|
||||
RadioConfig set_radio;
|
||||
User set_owner;
|
||||
ChannelSettings set_channel;
|
||||
} variant;
|
||||
} ToRadio;
|
||||
|
||||
@@ -266,10 +273,22 @@ typedef struct _ToRadio {
|
||||
#define _LocationSharing_MAX LocationSharing_LocDisabled
|
||||
#define _LocationSharing_ARRAYSIZE ((LocationSharing)(LocationSharing_LocDisabled+1))
|
||||
|
||||
#define _CriticalErrorCode_MIN CriticalErrorCode_None
|
||||
#define _CriticalErrorCode_MAX CriticalErrorCode_InvalidRadioSetting
|
||||
#define _CriticalErrorCode_ARRAYSIZE ((CriticalErrorCode)(CriticalErrorCode_InvalidRadioSetting+1))
|
||||
|
||||
#define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_Bw125Cr45Sf128
|
||||
#define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_Bw125Cr48Sf4096
|
||||
#define _ChannelSettings_ModemConfig_ARRAYSIZE ((ChannelSettings_ModemConfig)(ChannelSettings_ModemConfig_Bw125Cr48Sf4096+1))
|
||||
|
||||
#define _LogRecord_Level_MIN LogRecord_Level_UNSET
|
||||
#define _LogRecord_Level_MAX LogRecord_Level_CRITICAL
|
||||
#define _LogRecord_Level_ARRAYSIZE ((LogRecord_Level)(LogRecord_Level_CRITICAL+1))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define Position_init_default {0, 0, 0, 0, 0}
|
||||
@@ -277,14 +296,13 @@ typedef struct _ToRadio {
|
||||
#define User_init_default {"", "", "", {0}}
|
||||
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#define SubPacket_init_default {0, {Position_init_default}, 0, 0, 0, 0, {0}, 0}
|
||||
#define MeshPacket_init_default {0, 0, 0, {SubPacket_init_default}, 0, 0, 0, 0, 0}
|
||||
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0}
|
||||
#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, "", "", 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, _LocationSharing_MIN, _GpsOperation_MIN, 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, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define DeviceState_init_default {false, RadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0}
|
||||
#define DebugString_init_default {""}
|
||||
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
|
||||
#define FromRadio_init_default {0, 0, {MeshPacket_init_default}}
|
||||
#define ToRadio_init_default {0, {MeshPacket_init_default}}
|
||||
#define Position_init_zero {0, 0, 0, 0, 0}
|
||||
@@ -292,29 +310,34 @@ typedef struct _ToRadio {
|
||||
#define User_init_zero {"", "", "", {0}}
|
||||
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
|
||||
#define SubPacket_init_zero {0, {Position_init_zero}, 0, 0, 0, 0, {0}, 0}
|
||||
#define MeshPacket_init_zero {0, 0, 0, {SubPacket_init_zero}, 0, 0, 0, 0, 0}
|
||||
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0, {0}}, "", 0, 0, 0, 0}
|
||||
#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, "", "", 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, _LocationSharing_MIN, _GpsOperation_MIN, 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, "", "", "", 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define DeviceState_init_zero {false, RadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0}
|
||||
#define DebugString_init_zero {""}
|
||||
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
|
||||
#define FromRadio_init_zero {0, 0, {MeshPacket_init_zero}}
|
||||
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define ChannelSettings_tx_power_tag 1
|
||||
#define ChannelSettings_modem_config_tag 3
|
||||
#define ChannelSettings_psk_tag 4
|
||||
#define ChannelSettings_name_tag 5
|
||||
#define ChannelSettings_bandwidth_tag 6
|
||||
#define ChannelSettings_spread_factor_tag 7
|
||||
#define ChannelSettings_coding_rate_tag 8
|
||||
#define ChannelSettings_channel_num_tag 9
|
||||
#define ChannelSettings_psk_tag 4
|
||||
#define ChannelSettings_name_tag 5
|
||||
#define ChannelSettings_id_tag 10
|
||||
#define ChannelSettings_uplink_enabled_tag 16
|
||||
#define ChannelSettings_downlink_enabled_tag 17
|
||||
#define Data_portnum_tag 1
|
||||
#define Data_payload_tag 2
|
||||
#define DebugString_message_tag 1
|
||||
#define LogRecord_message_tag 1
|
||||
#define LogRecord_time_tag 2
|
||||
#define LogRecord_source_tag 3
|
||||
#define LogRecord_level_tag 4
|
||||
#define MyNodeInfo_my_node_num_tag 1
|
||||
#define MyNodeInfo_has_gps_tag 2
|
||||
#define MyNodeInfo_num_channels_tag 3
|
||||
@@ -329,14 +352,13 @@ typedef struct _ToRadio {
|
||||
#define MyNodeInfo_node_num_bits_tag 12
|
||||
#define MyNodeInfo_message_timeout_msec_tag 13
|
||||
#define MyNodeInfo_min_app_version_tag 14
|
||||
#define Position_latitude_i_tag 7
|
||||
#define Position_longitude_i_tag 8
|
||||
#define Position_altitude_tag 3
|
||||
#define Position_battery_level_tag 4
|
||||
#define Position_latitude_i_tag 7
|
||||
#define Position_longitude_i_tag 8
|
||||
#define Position_time_tag 9
|
||||
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
|
||||
#define RadioConfig_UserPreferences_send_owner_interval_tag 2
|
||||
#define RadioConfig_UserPreferences_num_missed_to_fail_tag 3
|
||||
#define RadioConfig_UserPreferences_wait_bluetooth_secs_tag 4
|
||||
#define RadioConfig_UserPreferences_screen_on_secs_tag 5
|
||||
#define RadioConfig_UserPreferences_phone_timeout_secs_tag 6
|
||||
@@ -349,15 +371,15 @@ typedef struct _ToRadio {
|
||||
#define RadioConfig_UserPreferences_wifi_password_tag 13
|
||||
#define RadioConfig_UserPreferences_wifi_ap_mode_tag 14
|
||||
#define RadioConfig_UserPreferences_region_tag 15
|
||||
#define RadioConfig_UserPreferences_location_share_tag 32
|
||||
#define RadioConfig_UserPreferences_gps_operation_tag 33
|
||||
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
|
||||
#define RadioConfig_UserPreferences_gps_attempt_time_tag 36
|
||||
#define RadioConfig_UserPreferences_is_router_tag 37
|
||||
#define RadioConfig_UserPreferences_is_low_power_tag 38
|
||||
#define RadioConfig_UserPreferences_fixed_position_tag 39
|
||||
#define RadioConfig_UserPreferences_factory_reset_tag 100
|
||||
#define RadioConfig_UserPreferences_debug_log_enabled_tag 101
|
||||
#define RadioConfig_UserPreferences_location_share_tag 32
|
||||
#define RadioConfig_UserPreferences_gps_operation_tag 33
|
||||
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
|
||||
#define RadioConfig_UserPreferences_gps_attempt_time_tag 36
|
||||
#define RadioConfig_UserPreferences_ignore_incoming_tag 103
|
||||
#define RouteDiscovery_route_tag 2
|
||||
#define User_id_tag 1
|
||||
@@ -367,8 +389,8 @@ typedef struct _ToRadio {
|
||||
#define NodeInfo_num_tag 1
|
||||
#define NodeInfo_user_tag 2
|
||||
#define NodeInfo_position_tag 3
|
||||
#define NodeInfo_snr_tag 7
|
||||
#define NodeInfo_next_hop_tag 5
|
||||
#define NodeInfo_snr_tag 7
|
||||
#define RadioConfig_preferences_tag 1
|
||||
#define RadioConfig_channel_settings_tag 2
|
||||
#define SubPacket_position_tag 1
|
||||
@@ -377,42 +399,36 @@ typedef struct _ToRadio {
|
||||
#define SubPacket_route_request_tag 6
|
||||
#define SubPacket_route_reply_tag 7
|
||||
#define SubPacket_route_error_tag 13
|
||||
#define SubPacket_success_id_tag 10
|
||||
#define SubPacket_fail_id_tag 11
|
||||
#define SubPacket_original_id_tag 2
|
||||
#define SubPacket_want_response_tag 5
|
||||
#define SubPacket_dest_tag 9
|
||||
#define SubPacket_success_id_tag 10
|
||||
#define SubPacket_fail_id_tag 11
|
||||
#define SubPacket_source_tag 12
|
||||
#define SubPacket_original_id_tag 2
|
||||
#define MeshPacket_decoded_tag 3
|
||||
#define MeshPacket_encrypted_tag 8
|
||||
#define MeshPacket_from_tag 1
|
||||
#define MeshPacket_to_tag 2
|
||||
#define MeshPacket_decoded_tag 3
|
||||
#define MeshPacket_encrypted_tag 8
|
||||
#define MeshPacket_channel_index_tag 4
|
||||
#define MeshPacket_id_tag 6
|
||||
#define MeshPacket_rx_time_tag 9
|
||||
#define MeshPacket_rx_snr_tag 7
|
||||
#define MeshPacket_rx_time_tag 9
|
||||
#define MeshPacket_hop_limit_tag 10
|
||||
#define MeshPacket_want_ack_tag 11
|
||||
#define DeviceState_radio_tag 1
|
||||
#define DeviceState_my_node_tag 2
|
||||
#define DeviceState_owner_tag 3
|
||||
#define DeviceState_node_db_tag 4
|
||||
#define DeviceState_receive_queue_tag 5
|
||||
#define DeviceState_version_tag 8
|
||||
#define DeviceState_rx_text_message_tag 7
|
||||
#define DeviceState_no_save_tag 9
|
||||
#define DeviceState_did_gps_reset_tag 11
|
||||
#define FromRadio_num_tag 1
|
||||
#define FromRadio_packet_tag 2
|
||||
#define FromRadio_my_info_tag 3
|
||||
#define FromRadio_node_info_tag 4
|
||||
#define FromRadio_radio_tag 6
|
||||
#define FromRadio_debug_string_tag 7
|
||||
#define FromRadio_log_record_tag 7
|
||||
#define FromRadio_config_complete_id_tag 8
|
||||
#define FromRadio_rebooted_tag 9
|
||||
#define FromRadio_num_tag 1
|
||||
#define FromRadio_channel_tag 10
|
||||
#define ToRadio_packet_tag 1
|
||||
#define ToRadio_want_config_id_tag 100
|
||||
#define ToRadio_set_radio_tag 101
|
||||
#define ToRadio_set_owner_tag 102
|
||||
#define ToRadio_set_channel_tag 103
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define Position_FIELDLIST(X, a) \
|
||||
@@ -449,7 +465,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload,data,data), 3) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,user,user), 4) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,route_request,route_request), 6) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,route_reply,route_reply), 7) \
|
||||
X(a, STATIC, ONEOF, ENUM, (payload,route_error,route_error), 13) \
|
||||
X(a, STATIC, ONEOF, UENUM, (payload,route_error,route_error), 13) \
|
||||
X(a, STATIC, SINGULAR, UINT32, original_id, 2) \
|
||||
X(a, STATIC, SINGULAR, BOOL, want_response, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, dest, 9) \
|
||||
@@ -469,6 +485,7 @@ X(a, STATIC, SINGULAR, UINT32, from, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, to, 2) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,decoded,decoded), 3) \
|
||||
X(a, STATIC, ONEOF, BYTES, (payload,encrypted,encrypted), 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, channel_index, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, id, 6) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, rx_snr, 7) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, rx_time, 9) \
|
||||
@@ -486,7 +503,10 @@ X(a, STATIC, SINGULAR, STRING, name, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, bandwidth, 6) \
|
||||
X(a, STATIC, SINGULAR, UINT32, spread_factor, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, coding_rate, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, channel_num, 9)
|
||||
X(a, STATIC, SINGULAR, UINT32, channel_num, 9) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, id, 10) \
|
||||
X(a, STATIC, SINGULAR, BOOL, uplink_enabled, 16) \
|
||||
X(a, STATIC, SINGULAR, BOOL, downlink_enabled, 17)
|
||||
#define ChannelSettings_CALLBACK NULL
|
||||
#define ChannelSettings_DEFAULT NULL
|
||||
|
||||
@@ -501,7 +521,6 @@ X(a, STATIC, OPTIONAL, MESSAGE, channel_settings, 2)
|
||||
#define RadioConfig_UserPreferences_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, position_broadcast_secs, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, send_owner_interval, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, num_missed_to_fail, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, wait_bluetooth_secs, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, screen_on_secs, 5) \
|
||||
X(a, STATIC, SINGULAR, UINT32, phone_timeout_secs, 6) \
|
||||
@@ -545,7 +564,7 @@ X(a, STATIC, SINGULAR, INT32, num_channels, 3) \
|
||||
X(a, STATIC, SINGULAR, STRING, region, 4) \
|
||||
X(a, STATIC, SINGULAR, STRING, hw_model, 5) \
|
||||
X(a, STATIC, SINGULAR, STRING, firmware_version, 6) \
|
||||
X(a, STATIC, SINGULAR, UINT32, error_code, 7) \
|
||||
X(a, STATIC, SINGULAR, UENUM, error_code, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, error_address, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, error_count, 9) \
|
||||
X(a, STATIC, SINGULAR, UINT32, packet_id_bits, 10) \
|
||||
@@ -556,29 +575,13 @@ X(a, STATIC, SINGULAR, UINT32, min_app_version, 14)
|
||||
#define MyNodeInfo_CALLBACK NULL
|
||||
#define MyNodeInfo_DEFAULT NULL
|
||||
|
||||
#define DeviceState_FIELDLIST(X, a) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, radio, 1) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, my_node, 2) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, version, 8) \
|
||||
X(a, STATIC, SINGULAR, BOOL, no_save, 9) \
|
||||
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11)
|
||||
#define DeviceState_CALLBACK NULL
|
||||
#define DeviceState_DEFAULT NULL
|
||||
#define DeviceState_radio_MSGTYPE RadioConfig
|
||||
#define DeviceState_my_node_MSGTYPE MyNodeInfo
|
||||
#define DeviceState_owner_MSGTYPE User
|
||||
#define DeviceState_node_db_MSGTYPE NodeInfo
|
||||
#define DeviceState_receive_queue_MSGTYPE MeshPacket
|
||||
#define DeviceState_rx_text_message_MSGTYPE MeshPacket
|
||||
|
||||
#define DebugString_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, STRING, message, 1)
|
||||
#define DebugString_CALLBACK NULL
|
||||
#define DebugString_DEFAULT NULL
|
||||
#define LogRecord_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, STRING, message, 1) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, time, 2) \
|
||||
X(a, STATIC, SINGULAR, STRING, source, 3) \
|
||||
X(a, STATIC, SINGULAR, UENUM, level, 4)
|
||||
#define LogRecord_CALLBACK NULL
|
||||
#define LogRecord_DEFAULT NULL
|
||||
|
||||
#define FromRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, num, 1) \
|
||||
@@ -586,27 +589,31 @@ X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 2) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,my_info,variant.my_info), 3) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,node_info,variant.node_info), 4) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,radio,variant.radio), 6) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,debug_string,variant.debug_string), 7) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,log_record,variant.log_record), 7) \
|
||||
X(a, STATIC, ONEOF, UINT32, (variant,config_complete_id,variant.config_complete_id), 8) \
|
||||
X(a, STATIC, ONEOF, BOOL, (variant,rebooted,variant.rebooted), 9)
|
||||
X(a, STATIC, ONEOF, BOOL, (variant,rebooted,variant.rebooted), 9) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,channel,variant.channel), 10)
|
||||
#define FromRadio_CALLBACK NULL
|
||||
#define FromRadio_DEFAULT NULL
|
||||
#define FromRadio_variant_packet_MSGTYPE MeshPacket
|
||||
#define FromRadio_variant_my_info_MSGTYPE MyNodeInfo
|
||||
#define FromRadio_variant_node_info_MSGTYPE NodeInfo
|
||||
#define FromRadio_variant_radio_MSGTYPE RadioConfig
|
||||
#define FromRadio_variant_debug_string_MSGTYPE DebugString
|
||||
#define FromRadio_variant_log_record_MSGTYPE LogRecord
|
||||
#define FromRadio_variant_channel_MSGTYPE ChannelSettings
|
||||
|
||||
#define ToRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 1) \
|
||||
X(a, STATIC, ONEOF, UINT32, (variant,want_config_id,variant.want_config_id), 100) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_radio,variant.set_radio), 101) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_owner,variant.set_owner), 102)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_owner,variant.set_owner), 102) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (variant,set_channel,variant.set_channel), 103)
|
||||
#define ToRadio_CALLBACK NULL
|
||||
#define ToRadio_DEFAULT NULL
|
||||
#define ToRadio_variant_packet_MSGTYPE MeshPacket
|
||||
#define ToRadio_variant_set_radio_MSGTYPE RadioConfig
|
||||
#define ToRadio_variant_set_owner_MSGTYPE User
|
||||
#define ToRadio_variant_set_channel_MSGTYPE ChannelSettings
|
||||
|
||||
extern const pb_msgdesc_t Position_msg;
|
||||
extern const pb_msgdesc_t Data_msg;
|
||||
@@ -619,8 +626,7 @@ extern const pb_msgdesc_t RadioConfig_msg;
|
||||
extern const pb_msgdesc_t RadioConfig_UserPreferences_msg;
|
||||
extern const pb_msgdesc_t NodeInfo_msg;
|
||||
extern const pb_msgdesc_t MyNodeInfo_msg;
|
||||
extern const pb_msgdesc_t DeviceState_msg;
|
||||
extern const pb_msgdesc_t DebugString_msg;
|
||||
extern const pb_msgdesc_t LogRecord_msg;
|
||||
extern const pb_msgdesc_t FromRadio_msg;
|
||||
extern const pb_msgdesc_t ToRadio_msg;
|
||||
|
||||
@@ -636,8 +642,7 @@ extern const pb_msgdesc_t ToRadio_msg;
|
||||
#define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg
|
||||
#define NodeInfo_fields &NodeInfo_msg
|
||||
#define MyNodeInfo_fields &MyNodeInfo_msg
|
||||
#define DeviceState_fields &DeviceState_msg
|
||||
#define DebugString_fields &DebugString_msg
|
||||
#define LogRecord_fields &LogRecord_msg
|
||||
#define FromRadio_fields &FromRadio_msg
|
||||
#define ToRadio_fields &ToRadio_msg
|
||||
|
||||
@@ -647,16 +652,15 @@ extern const pb_msgdesc_t ToRadio_msg;
|
||||
#define User_size 72
|
||||
#define RouteDiscovery_size 88
|
||||
#define SubPacket_size 275
|
||||
#define MeshPacket_size 314
|
||||
#define ChannelSettings_size 84
|
||||
#define RadioConfig_size 314
|
||||
#define RadioConfig_UserPreferences_size 225
|
||||
#define MeshPacket_size 320
|
||||
#define ChannelSettings_size 95
|
||||
#define RadioConfig_size 319
|
||||
#define RadioConfig_UserPreferences_size 219
|
||||
#define NodeInfo_size 132
|
||||
#define MyNodeInfo_size 110
|
||||
#define DeviceState_size 5468
|
||||
#define DebugString_size 258
|
||||
#define FromRadio_size 323
|
||||
#define ToRadio_size 318
|
||||
#define MyNodeInfo_size 106
|
||||
#define LogRecord_size 81
|
||||
#define FromRadio_size 329
|
||||
#define ToRadio_size 323
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.1 */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#include "portnums.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.1 */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#ifndef PB_PORTNUMS_PB_H_INCLUDED
|
||||
#define PB_PORTNUMS_PB_H_INCLUDED
|
||||
@@ -9,10 +9,6 @@
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Enum definitions */
|
||||
typedef enum _PortNum {
|
||||
PortNum_UNKNOWN_APP = 0,
|
||||
@@ -20,16 +16,21 @@ typedef enum _PortNum {
|
||||
PortNum_REMOTE_HARDWARE_APP = 2,
|
||||
PortNum_POSITION_APP = 3,
|
||||
PortNum_NODEINFO_APP = 4,
|
||||
PortNum_PRIVATE_APP = 256,
|
||||
PortNum_IP_TUNNEL_APP = 1024
|
||||
PortNum_REPLY_APP = 32,
|
||||
PortNum_IP_TUNNEL_APP = 33,
|
||||
PortNum_PRIVATE_APP = 256
|
||||
} PortNum;
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _PortNum_MIN PortNum_UNKNOWN_APP
|
||||
#define _PortNum_MAX PortNum_IP_TUNNEL_APP
|
||||
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_IP_TUNNEL_APP+1))
|
||||
#define _PortNum_MAX PortNum_PRIVATE_APP
|
||||
#define _PortNum_ARRAYSIZE ((PortNum)(PortNum_PRIVATE_APP+1))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.1 */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#include "remote_hardware.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.1 */
|
||||
/* Generated by nanopb-0.4.4 */
|
||||
|
||||
#ifndef PB_REMOTE_HARDWARE_PB_H_INCLUDED
|
||||
#define PB_REMOTE_HARDWARE_PB_H_INCLUDED
|
||||
@@ -9,10 +9,6 @@
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Enum definitions */
|
||||
typedef enum _HardwareMessage_Type {
|
||||
HardwareMessage_Type_UNSET = 0,
|
||||
@@ -37,6 +33,10 @@ typedef struct _HardwareMessage {
|
||||
#define _HardwareMessage_Type_ARRAYSIZE ((HardwareMessage_Type)(HardwareMessage_Type_READ_GPIOS_REPLY+1))
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define HardwareMessage_init_default {_HardwareMessage_Type_MIN, 0, 0}
|
||||
#define HardwareMessage_init_zero {_HardwareMessage_Type_MIN, 0, 0}
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "mesh.pb.h"
|
||||
#include "mesh/generated/mesh.pb.h"
|
||||
#include "mesh/generated/deviceonly.pb.h"
|
||||
|
||||
// this file defines constants which come from mesh.options
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "meshwifi/meshhttp.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "airtime.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "meshhttpStatic.h"
|
||||
@@ -56,8 +58,14 @@ 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);
|
||||
@@ -70,10 +78,12 @@ uint32_t timeSpeedUp = 0;
|
||||
|
||||
// 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
|
||||
char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"}, {".js", "text/javascript"},
|
||||
{".png", "image/png"}, {".jpg", "image/jpg"}, {".gz", "application/gzip"},
|
||||
{".gif", "image/gif"}, {".json", "application/json"}, {".css", "text/css"},
|
||||
{"", ""}};
|
||||
char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"},
|
||||
{".js", "text/javascript"}, {".png", "image/png"},
|
||||
{".jpg", "image/jpg"}, {".gz", "application/gzip"},
|
||||
{".gif", "image/gif"}, {".json", "application/json"},
|
||||
{".css", "text/css"}, {".ico", "image/vnd.microsoft.icon"},
|
||||
{".svg", "image/svg+xml"}, {"", ""}};
|
||||
|
||||
void handleWebResponse()
|
||||
{
|
||||
@@ -122,11 +132,12 @@ void taskCreateCert(void *parameter)
|
||||
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();
|
||||
// disableCore1WDT();
|
||||
yield();
|
||||
int createCertResult = createSelfSignedCert(*cert, KEYSIZE_2048, "CN=meshtastic.local,O=Meshtastic,C=US",
|
||||
"20190101000000", "20300101000000");
|
||||
// enableCore1WDT();
|
||||
yield();
|
||||
|
||||
if (createCertResult != 0) {
|
||||
DEBUG_MSG("Creating the certificate failed\n");
|
||||
@@ -175,9 +186,10 @@ void createSSLCert()
|
||||
NULL); /* Task handle. */
|
||||
|
||||
DEBUG_MSG("Waiting for SSL Cert to be generated.\n");
|
||||
if (isCertReady) {
|
||||
DEBUG_MSG(".\n");
|
||||
delayMicroseconds(1000);
|
||||
while (!isCertReady) {
|
||||
DEBUG_MSG(".");
|
||||
delay(1000);
|
||||
yield();
|
||||
}
|
||||
DEBUG_MSG("SSL Cert Ready!\n");
|
||||
}
|
||||
@@ -233,8 +245,14 @@ void initWebServer()
|
||||
ResourceNode *nodeStaticBrowse = new ResourceNode("/static", "GET", &handleStaticBrowse);
|
||||
ResourceNode *nodeStaticPOST = new ResourceNode("/static", "POST", &handleStaticPost);
|
||||
ResourceNode *nodeStatic = new ResourceNode("/static/*", "GET", &handleStatic);
|
||||
ResourceNode *nodeRestart = new ResourceNode("/restart", "POST", &handleRestart);
|
||||
ResourceNode *node404 = new ResourceNode("", "GET", &handle404);
|
||||
ResourceNode *nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload);
|
||||
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 *nodeJsonDelete = new ResourceNode("/json/spiffs/delete/static", "DELETE", &handleSpiffsDeleteStatic);
|
||||
|
||||
// Secure nodes
|
||||
secureServer->registerNode(nodeAPIv1ToRadioOptions);
|
||||
@@ -246,8 +264,14 @@ void initWebServer()
|
||||
secureServer->registerNode(nodeStaticBrowse);
|
||||
secureServer->registerNode(nodeStaticPOST);
|
||||
secureServer->registerNode(nodeStatic);
|
||||
secureServer->registerNode(nodeRestart);
|
||||
secureServer->registerNode(nodeFormUpload);
|
||||
secureServer->registerNode(nodeJsonScanNetworks);
|
||||
secureServer->registerNode(nodeJsonBlinkLED);
|
||||
secureServer->registerNode(nodeJsonSpiffsBrowseStatic);
|
||||
secureServer->registerNode(nodeJsonDelete);
|
||||
secureServer->registerNode(nodeJsonReport);
|
||||
secureServer->setDefaultNode(node404);
|
||||
secureServer->setDefaultNode(nodeFormUpload);
|
||||
|
||||
secureServer->addMiddleware(&middlewareSpeedUp240);
|
||||
|
||||
@@ -261,8 +285,14 @@ void initWebServer()
|
||||
insecureServer->registerNode(nodeStaticBrowse);
|
||||
insecureServer->registerNode(nodeStaticPOST);
|
||||
insecureServer->registerNode(nodeStatic);
|
||||
insecureServer->registerNode(nodeRestart);
|
||||
insecureServer->registerNode(nodeFormUpload);
|
||||
insecureServer->registerNode(nodeJsonScanNetworks);
|
||||
insecureServer->registerNode(nodeJsonBlinkLED);
|
||||
insecureServer->registerNode(nodeJsonSpiffsBrowseStatic);
|
||||
insecureServer->registerNode(nodeJsonDelete);
|
||||
insecureServer->registerNode(nodeJsonReport);
|
||||
insecureServer->setDefaultNode(node404);
|
||||
insecureServer->setDefaultNode(nodeFormUpload);
|
||||
|
||||
insecureServer->addMiddleware(&middlewareSpeedUp160);
|
||||
|
||||
@@ -282,6 +312,10 @@ void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<voi
|
||||
// We want to print the response status, so we need to call next() first.
|
||||
next();
|
||||
|
||||
// Phone (or other device) has contacted us over WiFi. Keep the radio turned on.
|
||||
// TODO: This should go into its own middleware layer separate from the speedup.
|
||||
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
|
||||
|
||||
setCpuFrequencyMhz(240);
|
||||
timeSpeedUp = millis();
|
||||
}
|
||||
@@ -291,6 +325,10 @@ void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<voi
|
||||
// We want to print the response status, so we need to call next() first.
|
||||
next();
|
||||
|
||||
// Phone (or other device) has contacted us over WiFi. Keep the radio turned on.
|
||||
// TODO: This should go into its own middleware layer separate from the speedup.
|
||||
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
|
||||
|
||||
// If the frequency is 240mhz, we have recently gotten a HTTPS request.
|
||||
// In that case, leave the frequency where it is and just update the
|
||||
// countdown timer (timeSpeedUp).
|
||||
@@ -369,6 +407,84 @@ void handleStaticPost(HTTPRequest *req, HTTPResponse *res)
|
||||
res->println("</body></html>");
|
||||
}
|
||||
|
||||
void handleSpiffsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
// jm
|
||||
|
||||
res->setHeader("Content-Type", "application/json");
|
||||
// res->setHeader("Content-Type", "text/html");
|
||||
|
||||
File root = SPIFFS.open("/");
|
||||
|
||||
if (root.isDirectory()) {
|
||||
res->println("{");
|
||||
res->println("\"data\": {");
|
||||
|
||||
File file = root.openNextFile();
|
||||
res->print("\"files\": [");
|
||||
bool firstFile = 1;
|
||||
while (file) {
|
||||
String filePath = String(file.name());
|
||||
if (filePath.indexOf("/static") == 0) {
|
||||
if (firstFile) {
|
||||
firstFile = 0;
|
||||
} else {
|
||||
res->println(",");
|
||||
}
|
||||
|
||||
res->println("{");
|
||||
|
||||
if (String(file.name()).substring(1).endsWith(".gz")) {
|
||||
String modifiedFile = String(file.name()).substring(1);
|
||||
modifiedFile.remove((modifiedFile.length() - 3), 3);
|
||||
res->print("\"nameModified\": \"" + modifiedFile + "\",");
|
||||
res->print("\"name\": \"" + String(file.name()).substring(1) + "\",");
|
||||
|
||||
} else {
|
||||
res->print("\"name\": \"" + String(file.name()).substring(1) + "\",");
|
||||
}
|
||||
res->print("\"size\": " + String(file.size()));
|
||||
res->print("}");
|
||||
}
|
||||
|
||||
file = root.openNextFile();
|
||||
}
|
||||
res->print("],");
|
||||
res->print("\"filesystem\" : {");
|
||||
res->print("\"total\" : " + String(SPIFFS.totalBytes()) + ",");
|
||||
res->print("\"used\" : " + String(SPIFFS.usedBytes()) + ",");
|
||||
res->print("\"free\" : " + String(SPIFFS.totalBytes() - SPIFFS.usedBytes()));
|
||||
res->println("}");
|
||||
res->println("},");
|
||||
res->println("\"status\": \"ok\"");
|
||||
res->println("}");
|
||||
}
|
||||
}
|
||||
|
||||
void handleSpiffsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
ResourceParameters *params = req->getParams();
|
||||
std::string paramValDelete;
|
||||
|
||||
res->setHeader("Content-Type", "application/json");
|
||||
if (params->getQueryParameter("delete", paramValDelete)) {
|
||||
std::string pathDelete = "/" + paramValDelete;
|
||||
if (SPIFFS.remove(pathDelete.c_str())) {
|
||||
Serial.println(pathDelete.c_str());
|
||||
res->println("{");
|
||||
res->println("\"status\": \"ok\"");
|
||||
res->println("}");
|
||||
return;
|
||||
} else {
|
||||
Serial.println(pathDelete.c_str());
|
||||
res->println("{");
|
||||
res->println("\"status\": \"Error\"");
|
||||
res->println("}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
// Get access to the parameters
|
||||
@@ -512,7 +628,6 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
// Get access to the parameters
|
||||
ResourceParameters *params = req->getParams();
|
||||
|
||||
|
||||
std::string parameter1;
|
||||
// Print the first parameter value
|
||||
if (params->getPathParameter(0, parameter1)) {
|
||||
@@ -560,7 +675,7 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
cTypeIdx += 1;
|
||||
} while (strlen(contentTypes[cTypeIdx][0]) > 0);
|
||||
|
||||
if(!has_set_content_type) {
|
||||
if (!has_set_content_type) {
|
||||
// Set a default content type
|
||||
res->setHeader("Content-Type", "application/octet-stream");
|
||||
}
|
||||
@@ -839,15 +954,13 @@ void handleRoot(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
|
||||
randomSeed(millis());
|
||||
|
||||
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());
|
||||
// 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";
|
||||
@@ -858,6 +971,10 @@ void handleRoot(HTTPRequest *req, HTTPResponse *res)
|
||||
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/issues/552>web interface</a> documentation.</p>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -878,7 +995,6 @@ void handleRoot(HTTPRequest *req, HTTPResponse *res)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Read the file from SPIFFS and write it to the HTTP response body
|
||||
size_t length = 0;
|
||||
do {
|
||||
@@ -889,6 +1005,182 @@ void handleRoot(HTTPRequest *req, HTTPResponse *res)
|
||||
} while (length > 0);
|
||||
}
|
||||
|
||||
void handleRestart(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
|
||||
DEBUG_MSG("***** Restarted on HTTP(s) Request *****\n");
|
||||
res->println("Restarting");
|
||||
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
void handleBlinkLED(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
res->setHeader("Content-Type", "application/json");
|
||||
|
||||
ResourceParameters *params = req->getParams();
|
||||
std::string blink_target;
|
||||
|
||||
if (!params->getQueryParameter("blink_target", blink_target)) {
|
||||
// if no blink_target was supplied in the URL parameters of the
|
||||
// POST request, then assume we should blink the LED
|
||||
blink_target = "LED";
|
||||
}
|
||||
|
||||
if (blink_target == "LED") {
|
||||
uint8_t count = 10;
|
||||
while (count > 0) {
|
||||
setLed(true);
|
||||
delay(50);
|
||||
setLed(false);
|
||||
delay(50);
|
||||
count = count - 1;
|
||||
}
|
||||
} else {
|
||||
screen->blink();
|
||||
}
|
||||
|
||||
res->println("{");
|
||||
res->println("\"status\": \"ok\"");
|
||||
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("Content-Type", "text/html");
|
||||
|
||||
int n = WiFi.scanNetworks();
|
||||
res->println("{");
|
||||
res->println("\"data\": {");
|
||||
if (n == 0) {
|
||||
// No networks found.
|
||||
res->println("\"networks\": []");
|
||||
|
||||
} else {
|
||||
res->println("\"networks\": [");
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
char ssidArray[50];
|
||||
String ssidString = String(WiFi.SSID(i));
|
||||
// String ssidString = String(WiFi.SSID(i)).toCharArray(ssidArray, WiFi.SSID(i).length());
|
||||
ssidString.replace("\"", "\\\"");
|
||||
ssidString.toCharArray(ssidArray, 50);
|
||||
|
||||
if (WiFi.encryptionType(i) != WIFI_AUTH_OPEN) {
|
||||
// res->println("{\"ssid\": \"%s\",\"rssi\": -75}, ", String(WiFi.SSID(i).c_str() );
|
||||
|
||||
res->printf("{\"ssid\": \"%s\",\"rssi\": %d}", ssidArray, WiFi.RSSI(i));
|
||||
// WiFi.RSSI(i)
|
||||
if (i != n - 1) {
|
||||
res->printf(",");
|
||||
}
|
||||
}
|
||||
// Yield some cpu cycles to IP stack.
|
||||
// This is important in case the list is large and it takes us time to return
|
||||
// to the main loop.
|
||||
yield();
|
||||
}
|
||||
res->println("]");
|
||||
}
|
||||
res->println("},");
|
||||
res->println("\"status\": \"ok\"");
|
||||
res->println("}");
|
||||
}
|
||||
|
||||
void handleFavicon(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
// Set Content-Type
|
||||
@@ -897,7 +1189,6 @@ void handleFavicon(HTTPRequest *req, HTTPResponse *res)
|
||||
res->write(FAVICON_DATA, FAVICON_LENGTH);
|
||||
}
|
||||
|
||||
|
||||
void replaceAll(std::string &str, const std::string &from, const std::string &to)
|
||||
{
|
||||
if (from.empty())
|
||||
@@ -908,4 +1199,3 @@ void replaceAll(std::string &str, const std::string &from, const std::string &to
|
||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,13 +21,33 @@ uint8_t wifiDisconnectReason = 0;
|
||||
// Stores our hostname
|
||||
char ourHost[16];
|
||||
|
||||
bool forcedSoftAP = 0;
|
||||
|
||||
bool APStartupComplete = 0;
|
||||
|
||||
bool isSoftAPForced()
|
||||
{
|
||||
return forcedSoftAP;
|
||||
}
|
||||
|
||||
bool isWifiAvailable()
|
||||
{
|
||||
// If wifi status is connected, return true regardless of the radio configuration.
|
||||
if (isSoftAPForced()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||
|
||||
// strcpy(radioConfig.preferences.wifi_ssid, "");
|
||||
// strcpy(radioConfig.preferences.wifi_password, "");
|
||||
// strcpy(radioConfig.preferences.wifi_ssid, "meshtastic");
|
||||
// strcpy(radioConfig.preferences.wifi_password, "meshtastic!");
|
||||
|
||||
// strcpy(radioConfig.preferences.wifi_ssid, "meshtasticAdmin");
|
||||
// strcpy(radioConfig.preferences.wifi_password, "12345678");
|
||||
|
||||
// radioConfig.preferences.wifi_ap_mode = true;
|
||||
// radioConfig.preferences.wifi_ap_mode = false;
|
||||
|
||||
if (*wifiName && *wifiPsw) {
|
||||
return 1;
|
||||
@@ -58,20 +78,44 @@ void deinitWifi()
|
||||
}
|
||||
|
||||
// Startup WiFi
|
||||
void initWifi()
|
||||
void initWifi(bool forceSoftAP)
|
||||
{
|
||||
if (isWifiAvailable() == 0) {
|
||||
return;
|
||||
|
||||
if (forceSoftAP) {
|
||||
// do nothing
|
||||
// DEBUG_MSG("----- Forcing SoftAP\n");
|
||||
} else {
|
||||
if (isWifiAvailable() == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
forcedSoftAP = forceSoftAP;
|
||||
|
||||
createSSLCert();
|
||||
|
||||
if (radioConfig.has_preferences) {
|
||||
if (radioConfig.has_preferences || forceSoftAP) {
|
||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||
|
||||
if (*wifiName && *wifiPsw) {
|
||||
if (radioConfig.preferences.wifi_ap_mode) {
|
||||
if ((*wifiName && *wifiPsw) || forceSoftAP) {
|
||||
if (forceSoftAP) {
|
||||
|
||||
DEBUG_MSG("Forcing SoftAP\n");
|
||||
|
||||
const char *softAPssid = "meshtasticAdmin";
|
||||
const char *softAPpasswd = "12345678";
|
||||
|
||||
IPAddress apIP(192, 168, 42, 1);
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
|
||||
WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
|
||||
DEBUG_MSG("STARTING WIFI AP: ssid=%s, ok=%d\n", softAPssid, WiFi.softAP(softAPssid, softAPpasswd));
|
||||
DEBUG_MSG("MY IP ADDRESS: %s\n", WiFi.softAPIP().toString().c_str());
|
||||
|
||||
dnsServer.start(53, "*", apIP);
|
||||
|
||||
} else if (radioConfig.preferences.wifi_ap_mode) {
|
||||
|
||||
IPAddress apIP(192, 168, 42, 1);
|
||||
WiFi.onEvent(WiFiEvent);
|
||||
@@ -179,9 +223,16 @@ static void WiFiEvent(WiFiEvent_t event)
|
||||
DEBUG_MSG("Obtained IP address: \n");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
// Start web server
|
||||
initWebServer();
|
||||
initApiServer();
|
||||
if (!APStartupComplete) {
|
||||
// Start web server
|
||||
DEBUG_MSG("... Starting network services\n");
|
||||
initWebServer();
|
||||
initApiServer();
|
||||
|
||||
APStartupComplete = true;
|
||||
} else {
|
||||
DEBUG_MSG("... Not starting network services (They're already running)\n");
|
||||
}
|
||||
|
||||
break;
|
||||
case SYSTEM_EVENT_STA_LOST_IP:
|
||||
@@ -203,9 +254,16 @@ static void WiFiEvent(WiFiEvent_t event)
|
||||
DEBUG_MSG("WiFi access point started\n");
|
||||
Serial.println(WiFi.softAPIP());
|
||||
|
||||
// Start web server
|
||||
initWebServer();
|
||||
initApiServer();
|
||||
if (!APStartupComplete) {
|
||||
// Start web server
|
||||
DEBUG_MSG("... Starting network services\n");
|
||||
initWebServer();
|
||||
initApiServer();
|
||||
|
||||
APStartupComplete = true;
|
||||
} else {
|
||||
DEBUG_MSG("... Not starting network services (They're already running)\n");
|
||||
}
|
||||
|
||||
break;
|
||||
case SYSTEM_EVENT_AP_STOP:
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
|
||||
void initWifi();
|
||||
void initWifi(bool forceSoftAP);
|
||||
void deinitWifi();
|
||||
|
||||
bool isWifiAvailable();
|
||||
@@ -18,5 +18,7 @@ void handleDNSResponse();
|
||||
|
||||
void reconnectWiFi();
|
||||
|
||||
bool isSoftAPForced();
|
||||
|
||||
uint8_t getWifiDisconnectReason();
|
||||
|
||||
|
||||
@@ -545,7 +545,7 @@ void setBluetoothEnable(bool on)
|
||||
if (firstTime) {
|
||||
firstTime = 0;
|
||||
} else {
|
||||
initWifi();
|
||||
initWifi(0);
|
||||
}
|
||||
} else {
|
||||
|
||||
|
||||
@@ -18,10 +18,10 @@ bool BQ25713::setup()
|
||||
if (!writeReg(0x00, 0x0F0A))
|
||||
return false; // Config Charge Option 0
|
||||
|
||||
if (!writeReg(0x02, 0x0224))
|
||||
if (!writeReg(0x02, 0x0224)) // 512mA, FIXME, this seems to be setting a number of bits marked as reserved, why?
|
||||
return false; // Config Charge Current
|
||||
|
||||
if (!writeReg(0x04, 0x1070))
|
||||
if (!writeReg(0x04, 0x1070)) // about 4.29V
|
||||
return false; // Config Charge Voltage
|
||||
|
||||
if (!writeReg(0x06, 0x099C))
|
||||
@@ -32,7 +32,7 @@ bool BQ25713::setup()
|
||||
|
||||
// if(!writeReg(0x0A,0x0100)) return false;//Config Input Voltage
|
||||
|
||||
if (!writeReg(0x0C, 0x1800))
|
||||
if (!writeReg(0x0C, 0x1800)) // 4.2Vish FIXME, we could lower this?
|
||||
return false; // Config Minimum System Voltage
|
||||
|
||||
if (!writeReg(0x0E, 0x4900))
|
||||
|
||||
@@ -51,7 +51,7 @@ void getMacAddr(uint8_t *dmac)
|
||||
NRF52Bluetooth *nrf52Bluetooth;
|
||||
|
||||
static bool bleOn = false;
|
||||
static const bool enableBle = false; // Set to false for easier debugging
|
||||
static const bool enableBle = true; // Set to false for easier debugging
|
||||
|
||||
void setBluetoothEnable(bool on)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "meshwifi/meshhttp.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
|
||||
void initWifi() {}
|
||||
void initWifi(bool forceSoftAP) {}
|
||||
|
||||
void deinitWifi() {}
|
||||
|
||||
|
||||
@@ -10,9 +10,49 @@ RemoteHardwarePlugin remoteHardwarePlugin;
|
||||
|
||||
#define NUM_GPIOS 64
|
||||
|
||||
// Because (FIXME) we currently don't tell API clients status on sent messages
|
||||
// we need to throttle our sending, so that if a gpio is bouncing up and down we
|
||||
// don't generate more messages than the net can send. So we limit watch messages to
|
||||
// a max of one change per 30 seconds
|
||||
#define WATCH_INTERVAL_MSEC (30 * 1000)
|
||||
|
||||
/// Set pin modes for every set bit in a mask
|
||||
static void pinModes(uint64_t mask, uint8_t mode) {
|
||||
for (uint8_t i = 0; i < NUM_GPIOS; i++) {
|
||||
if (mask & (1 << i)) {
|
||||
pinMode(i, mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read all the pins mentioned in a mask
|
||||
static uint64_t digitalReads(uint64_t mask) {
|
||||
uint64_t res = 0;
|
||||
|
||||
pinModes(mask, INPUT_PULLUP);
|
||||
|
||||
for (uint8_t i = 0; i < NUM_GPIOS; i++) {
|
||||
uint64_t m = 1 << i;
|
||||
if (mask & m) {
|
||||
if (digitalRead(i))
|
||||
res |= m;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
RemoteHardwarePlugin::RemoteHardwarePlugin()
|
||||
: ProtobufPlugin("remotehardware", PortNum_REMOTE_HARDWARE_APP, HardwareMessage_fields),
|
||||
concurrency::OSThread("remotehardware")
|
||||
{
|
||||
}
|
||||
|
||||
bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const HardwareMessage &p)
|
||||
{
|
||||
DEBUG_MSG("Received RemoteHardware typ=%d\n", p.typ);
|
||||
|
||||
switch (p.typ) {
|
||||
case HardwareMessage_Type_WRITE_GPIOS:
|
||||
// Print notification to LCD screen
|
||||
@@ -22,24 +62,17 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H
|
||||
uint64_t mask = 1 << i;
|
||||
if (p.gpio_mask & mask) {
|
||||
digitalWrite(i, (p.gpio_value & mask) ? 1 : 0);
|
||||
pinMode(i, OUTPUT);
|
||||
}
|
||||
}
|
||||
pinModes(p.gpio_mask, OUTPUT);
|
||||
|
||||
break;
|
||||
|
||||
|
||||
case HardwareMessage_Type_READ_GPIOS: {
|
||||
// Print notification to LCD screen
|
||||
screen->print("Read GPIOs\n");
|
||||
|
||||
uint64_t res = 0;
|
||||
for (uint8_t i = 0; i < NUM_GPIOS; i++) {
|
||||
uint64_t mask = 1 << i;
|
||||
if (p.gpio_mask & mask) {
|
||||
pinMode(i, INPUT_PULLUP);
|
||||
if (digitalRead(i))
|
||||
res |= (1 << i);
|
||||
}
|
||||
}
|
||||
uint64_t res = digitalReads(p.gpio_mask);
|
||||
|
||||
// Send the reply
|
||||
HardwareMessage reply = HardwareMessage_init_default;
|
||||
@@ -51,6 +84,15 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H
|
||||
break;
|
||||
}
|
||||
|
||||
case HardwareMessage_Type_WATCH_GPIOS: {
|
||||
watchGpios = p.gpio_mask;
|
||||
lastWatchMsec = 0; // Force a new publish soon
|
||||
previousWatch = ~watchGpios; // generate a 'previous' value which is guaranteed to not match (to force an initial publish)
|
||||
enabled = true; // Let our thread run at least once
|
||||
DEBUG_MSG("Now watching GPIOs 0x%llx\n", watchGpios);
|
||||
break;
|
||||
}
|
||||
|
||||
case HardwareMessage_Type_READ_GPIOS_REPLY:
|
||||
case HardwareMessage_Type_GPIOS_CHANGED:
|
||||
break; // Ignore - we might see our own replies
|
||||
@@ -59,5 +101,34 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, const H
|
||||
DEBUG_MSG("Hardware operation %d not yet implemented! FIXME\n", p.typ);
|
||||
break;
|
||||
}
|
||||
|
||||
return true; // handled
|
||||
}
|
||||
|
||||
int32_t RemoteHardwarePlugin::runOnce() {
|
||||
if(watchGpios) {
|
||||
uint32_t now = millis();
|
||||
|
||||
if(now - lastWatchMsec >= WATCH_INTERVAL_MSEC) {
|
||||
uint64_t curVal = digitalReads(watchGpios);
|
||||
|
||||
if(curVal != previousWatch) {
|
||||
previousWatch = curVal;
|
||||
DEBUG_MSG("Broadcasting GPIOS 0x%llx changed!\n", curVal);
|
||||
|
||||
// Something changed! Tell the world with a broadcast message
|
||||
HardwareMessage reply = HardwareMessage_init_default;
|
||||
reply.typ = HardwareMessage_Type_GPIOS_CHANGED;
|
||||
reply.gpio_value = curVal;
|
||||
MeshPacket *p = allocDataProtobuf(reply);
|
||||
service.sendToMesh(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// No longer watching anything - stop using CPU
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
return 200; // Poll our GPIOs every 200ms (FIXME, make adjustable via protobuf arg)
|
||||
}
|
||||
@@ -1,17 +1,26 @@
|
||||
#pragma once
|
||||
#include "ProtobufPlugin.h"
|
||||
#include "remote_hardware.pb.h"
|
||||
#include "mesh/generated/remote_hardware.pb.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
|
||||
/**
|
||||
* A plugin that provides easy low-level remote access to device hardware.
|
||||
*/
|
||||
class RemoteHardwarePlugin : public ProtobufPlugin<HardwareMessage>
|
||||
class RemoteHardwarePlugin : public ProtobufPlugin<HardwareMessage>, private concurrency::OSThread
|
||||
{
|
||||
/// The current set of GPIOs we've been asked to watch for changes
|
||||
uint64_t watchGpios = 0;
|
||||
|
||||
/// The previously read value of watched pins
|
||||
uint64_t previousWatch = 0;
|
||||
|
||||
/// The timestamp of our last watch event (we throttle watches to 1 change every 30 seconds)
|
||||
uint32_t lastWatchMsec = 0;
|
||||
public:
|
||||
/** Constructor
|
||||
* name is for debugging output
|
||||
*/
|
||||
RemoteHardwarePlugin() : ProtobufPlugin("remotehardware", PortNum_REMOTE_HARDWARE_APP, HardwareMessage_fields) {}
|
||||
RemoteHardwarePlugin();
|
||||
|
||||
protected:
|
||||
/** Called to handle a particular incoming message
|
||||
@@ -19,6 +28,16 @@ class RemoteHardwarePlugin : public ProtobufPlugin<HardwareMessage>
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const MeshPacket &mp, const HardwareMessage &p);
|
||||
|
||||
/**
|
||||
* Periodically read the gpios we have been asked to WATCH, if they have changed,
|
||||
* broadcast a message with the change information.
|
||||
*
|
||||
* The method that will be called each time our thread gets a chance to run
|
||||
*
|
||||
* Returns desired period for next invocation (or RUN_SAME for no change)
|
||||
*/
|
||||
virtual int32_t runOnce();
|
||||
};
|
||||
|
||||
extern RemoteHardwarePlugin remoteHardwarePlugin;
|
||||
27
src/plugins/ReplyPlugin.cpp
Normal file
27
src/plugins/ReplyPlugin.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "ReplyPlugin.h"
|
||||
#include "MeshService.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
|
||||
#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
|
||||
auto req = *currentRequest;
|
||||
auto &p = req.decoded.data;
|
||||
// The incoming message is in p.payload
|
||||
DEBUG_MSG("Received message from=0x%0x, id=%d, msg=%.*s\n", req.from, req.id, p.payload.size, p.payload.bytes);
|
||||
|
||||
screen->print("Sending reply\n");
|
||||
|
||||
const char *replyStr = "Message Received";
|
||||
auto reply = allocDataPacket(); // Allocate a packet for sending
|
||||
reply->decoded.data.payload.size = strlen(replyStr); // You must specify how many bytes are in the reply
|
||||
memcpy(reply->decoded.data.payload.bytes, replyStr, reply->decoded.data.payload.size);
|
||||
|
||||
return reply;
|
||||
}
|
||||
22
src/plugins/ReplyPlugin.h
Normal file
22
src/plugins/ReplyPlugin.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "SinglePortPlugin.h"
|
||||
|
||||
|
||||
/**
|
||||
* A simple example plugin that just replies with "Message received" to any message it receives.
|
||||
*/
|
||||
class ReplyPlugin : public SinglePortPlugin
|
||||
{
|
||||
public:
|
||||
/** Constructor
|
||||
* name is for debugging output
|
||||
*/
|
||||
ReplyPlugin() : SinglePortPlugin("reply", PortNum_REPLY_APP) {}
|
||||
|
||||
protected:
|
||||
|
||||
/** For reply plugin we do all of our processing in the (normally optional)
|
||||
* want_replies handling
|
||||
*/
|
||||
virtual MeshPacket *allocReply();
|
||||
};
|
||||
@@ -127,7 +127,7 @@ static void waitEnterSleep()
|
||||
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
|
||||
|
||||
if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
|
||||
recordCriticalError(ErrSleepEnterWait);
|
||||
recordCriticalError(CriticalErrorCode_SleepEnterWait);
|
||||
assert(0); // FIXME - for now we just restart, need to fix bug #167
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ static const uint8_t AREF = PIN_AREF;
|
||||
// #define PIN_GPS_WAKE 20 // CELL_CTRL in schematic? based on their example code
|
||||
#define PIN_GPS_EN 7 // GPS_EN active high
|
||||
|
||||
#define PIN_VUSB_EN 21
|
||||
// #define PIN_VUSB_EN 21
|
||||
|
||||
// LCD
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[VERSION]
|
||||
major = 1
|
||||
minor = 1
|
||||
build = 20
|
||||
build = 30
|
||||
|
||||
Reference in New Issue
Block a user